hdfs-mount/FileHandleReader_test.go

177 строки
6.2 KiB
Go

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
package main
import (
"bazil.org/fuse"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"io"
"math/rand"
"testing"
)
// Testing reading of an empty file
func TestEmptyFile(t *testing.T) {
mockCtrl := gomock.NewController(t)
hdfsReader := NewMockReadSeekCloser(mockCtrl)
handle := createTestHandle(t, mockCtrl, hdfsReader)
hdfsReader.whenReadReturn([]byte{}, io.EOF)
handle.readAndVerify(t, 0, 1024, []byte{})
hdfsReader.EXPECT().Close().Return(nil)
handle.Release(nil, nil)
}
// Testing reading of a small "HelloWorld!" file using few Read() operations
func TestSmallFileSequentialRead(t *testing.T) {
mockCtrl := gomock.NewController(t)
hdfsReader := NewMockReadSeekCloser(mockCtrl)
handle := createTestHandle(t, mockCtrl, hdfsReader)
hdfsReader.whenReadReturn([]byte("Hel"), nil)
hdfsReader.whenReadReturn([]byte("lo"), nil)
handle.readAndVerify(t, 0, 5, []byte("Hello"))
hdfsReader.whenReadReturn([]byte("World!"), nil)
handle.readAndVerify(t, 5, 6, []byte("World!"))
hdfsReader.whenReadReturn([]byte{}, io.EOF)
handle.readAndVerify(t, 11, 1024, []byte{})
hdfsReader.EXPECT().Close().Return(nil)
handle.Release(nil, nil)
}
// If reads are reordered but not far away from each other
// this should not cause Seek() on the backend HDFS reader
func TestReoderedReadsDontCauseSeek(t *testing.T) {
mockCtrl := gomock.NewController(t)
hdfsReader := NewMockReadSeekCloser(mockCtrl)
handle := createTestHandle(t, mockCtrl, hdfsReader)
hdfsReader.whenReadReturn([]byte("He"), nil)
handle.readAndVerify(t, 0, 2, []byte("He"))
hdfsReader.whenReadReturn([]byte("ll"), nil)
hdfsReader.whenReadReturn([]byte("oWorld!"), nil)
handle.readAndVerify(t, 8, 3, []byte("ld!"))
handle.readAndVerify(t, 2, 6, []byte("lloWor"))
hdfsReader.EXPECT().Close().Return(nil)
handle.Release(nil, nil)
}
// Seak()->Read()->Read()->Seek()->Read()
func TestSeekAndRead(t *testing.T) {
mockCtrl := gomock.NewController(t)
hdfsReader := NewMockReadSeekCloser(mockCtrl)
handle := createTestHandle(t, mockCtrl, hdfsReader)
hdfsReader.expectSeek(1000000)
hdfsReader.whenReadReturn([]byte("foo"), nil)
handle.readAndVerify(t, 1000000, 3, []byte("foo"))
hdfsReader.whenReadReturn([]byte("bar"), nil)
handle.readAndVerify(t, 1000003, 3, []byte("bar"))
hdfsReader.expectSeek(2000000)
hdfsReader.whenReadReturn([]byte("qux"), nil)
hdfsReader.whenReadReturn([]byte("baz"), nil)
handle.readAndVerify(t, 2000000, 6, []byte("quxbaz"))
hdfsReader.EXPECT().Close().Return(nil)
handle.Release(nil, nil)
}
// Testing of accessing a pseudo-random file of size 512K
// The goal of this test is to verify buffering and offset arithmetic
// For reads which are close to each other
func TestRandomAccess512K(t *testing.T) {
RandomAccess(t, 1024*1024/2, 4096)
}
// Testing of accessing a pseudo-random file of size 5G
// This is to ensure that 64-bit offset handling works as expected
func TestRandomAccess5G(t *testing.T) {
RandomAccess(t, 5*1024*1024*1024, 64*1024)
}
// Performing 1000 random reads on a virtual file with programmatically-generated content:
// The value of each byte is a simple function of its offset
func RandomAccess(t *testing.T, fileSize int64, maxRead int) {
mockCtrl := gomock.NewController(t)
r := rand.New(rand.NewSource(0))
hdfsReader := &MockReadSeekCloserWithPseudoRandomContent{FileSize: fileSize, Rand: r}
handle := createTestHandle(t, mockCtrl, hdfsReader)
for iter := 0; iter < 1000; iter++ {
// Generating a read of a random number of bytes from from random offset
offset := r.Int63n(fileSize)
size := r.Intn(maxRead) + 1
// Computing maximum expected number of bytes which can be returned
expectedMaxBytesRead := size
expectedMinBytesRead := size
if int64(expectedMinBytesRead) > fileSize-offset {
expectedMinBytesRead = int(fileSize - offset)
}
// Executing read request
resp := fuse.ReadResponse{Data: make([]byte, 0, size)}
err := handle.Read(nil, &fuse.ReadRequest{Offset: offset, Size: size}, &resp)
assert.Nil(t, err)
assert.NotNil(t, resp.Data)
actualBytesRead := len(resp.Data)
assert.True(t, actualBytesRead <= expectedMaxBytesRead)
assert.True(t, actualBytesRead >= expectedMinBytesRead)
if expectedMaxBytesRead > 0 {
assert.True(t, actualBytesRead != 0)
}
// verifying returned data
for i := offset; i < offset+int64(actualBytesRead); i++ {
if resp.Data[i-offset] != generateByteAtOffset(i) {
t.Error("Invalid byte at offset ", i-offset)
return
}
}
}
assert.False(t, hdfsReader.IsClosed)
handle.Release(nil, nil)
assert.True(t, hdfsReader.IsClosed)
}
///////////////// Test Helpers /////////////////////
// common setup for FileHandleReader testing
func createTestHandle(t *testing.T, mockCtrl *gomock.Controller, hdfsReader ReadSeekCloser) *FileHandle {
hdfsAccessor := NewMockHdfsAccessor(mockCtrl)
hdfsAccessor.EXPECT().Stat("/test.dat").Return(Attrs{Name: "test.dat"}, nil)
hdfsAccessor.EXPECT().OpenRead("/test.dat").Return(hdfsReader, nil)
fs, _ := NewFileSystem(hdfsAccessor, "/tmp/x", []string{"*"}, false, false, NewDefaultRetryPolicy(&MockClock{}), &MockClock{})
root, _ := fs.Root()
file, _ := root.(*Dir).Lookup(nil, "test.dat")
h, _ := file.(*File).Open(nil, &fuse.OpenRequest{Flags: fuse.OpenReadOnly}, nil)
return h.(*FileHandle)
}
// sets hdfsReader mock to respond on Read() request in a certain way
func (hdfsReader *MockReadSeekCloser) whenReadReturn(data []byte, err error) {
hdfsReader.EXPECT().Read(gomock.Any()).Do(
func(buf []byte) {
copy(buf, data)
}).Return(len(data), err)
}
// sets hdfsReader mock to respond on Read() request in a certain way
func (hdfsReader *MockReadSeekCloser) expectSeek(pos int64) {
hdfsReader.EXPECT().Seek(pos).Return(nil)
}
// issue a Read() request to a handle and check returned data
func (handle *FileHandle) readAndVerify(t *testing.T, offset int64, size int, data []byte) {
resp := fuse.ReadResponse{Data: make([]byte, 0, size)}
err := handle.Read(nil, &fuse.ReadRequest{Offset: offset, Size: size}, &resp)
assert.Nil(t, err)
assert.NotNil(t, resp.Data)
assert.Equal(t, len(data), len(resp.Data))
assert.Equal(t, data, resp.Data)
}