1
// Copyright 2022 The CubeFS Authors.
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
// implied. See the License for the specific language governing
13
// permissions and limitations under the License.
27
"github.com/brahma-adshonor/gohook"
28
"github.com/stretchr/testify/assert"
30
"github.com/cubefs/cubefs/blockcache/bcache"
31
"github.com/cubefs/cubefs/proto"
32
"github.com/cubefs/cubefs/sdk/data/manager"
33
"github.com/cubefs/cubefs/sdk/data/stream"
34
"github.com/cubefs/cubefs/sdk/meta"
35
"github.com/cubefs/cubefs/util/errors"
38
func TestNewReader(t *testing.T) {
39
mockConfig := ClientConfig{
56
ec := &stream.ExtentClient{}
57
err := gohook.HookMethod(ec, "Write", MockWriteTrue, nil)
59
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
63
reader := NewReader(mockConfig)
64
assert.NotEmpty(t, reader, nil)
67
func TestBuildExtentKey(t *testing.T) {
68
testCase := []struct {
70
expectEk proto.ExtentKey
72
{nil, proto.ExtentKey{}},
74
{FileOffset: uint64(0), Size: uint32(100)},
75
{FileOffset: uint64(100), Size: uint32(100)},
76
{FileOffset: uint64(200), Size: uint32(100)},
77
{FileOffset: uint64(300), Size: uint32(100)},
78
}, proto.ExtentKey{FileOffset: uint64(100), Size: uint32(100)}},
80
{FileOffset: uint64(0), Size: uint32(1)},
81
{FileOffset: uint64(1), Size: uint32(1)},
82
{FileOffset: uint64(2), Size: uint32(1)},
83
{FileOffset: uint64(3), Size: uint32(1)},
84
}, proto.ExtentKey{}},
88
rs.objExtentKey = proto.ObjExtentKey{FileOffset: 100, Size: 100}
89
for _, tc := range testCase {
91
reader.limitManager = manager.NewLimitManager(nil)
92
reader.extentKeys = tc.eks
93
reader.buildExtentKey(rs)
94
assert.Equal(t, tc.expectEk, rs.extentKey)
98
func TestFileSize(t *testing.T) {
99
testCase := []struct {
101
objEks []proto.ObjExtentKey
105
{false, nil, 0, false},
106
{true, nil, 0, true},
107
{true, []proto.ObjExtentKey{{Size: uint64(100), FileOffset: uint64(100)}}, 200, true},
110
for _, tc := range testCase {
112
reader.limitManager = manager.NewLimitManager(nil)
113
reader.valid = tc.valid
114
reader.objExtentKeys = tc.objEks
115
gotSize, gotOk := reader.fileSize()
116
assert.Equal(t, tc.expectSize, gotSize)
117
assert.Equal(t, tc.expectOk, gotOk)
120
//// mock objExtentKey
121
//objEks := make([]proto.ObjExtentKey, 0)
122
//objEkLen := rand.Intn(20)
123
//expectedFileSize := 0
124
//for i := 0; i < objEkLen; i++ {
125
// size := rand.Intn(1000)
126
// objEks = append(objEks, proto.ObjExtentKey{Size: uint64(size), FileOffset: uint64(expectedFileSize)})
127
// expectedFileSize += size
131
//mockConfig := ClientConfig{
140
// EnableBcache: false,
142
// ReadConcurrency: 0,
146
// CacheThreshold: 0,
148
//reader := NewReader(mockConfig)
149
//reader.valid = true
150
//reader.objExtentKeys = objEks
152
//got, ok := reader.fileSize()
153
//assert.True(t, true, ok)
154
//assert.Equal(t, expectedFileSize, int(got))
156
//ctx := context.Background()
160
func TestRefreshEbsExtents(t *testing.T) {
161
testCase := []struct {
162
getObjFunc func(*meta.MetaWrapper, uint64) (uint64, uint64, []proto.ExtentKey, []proto.ObjExtentKey, error)
165
{MockGetObjExtentsTrue, true},
166
{MockGetObjExtentsFalse, false},
169
for _, tc := range testCase {
171
reader.limitManager = manager.NewLimitManager(nil)
172
mw := &meta.MetaWrapper{}
173
err := gohook.HookMethod(mw, "GetObjExtents", tc.getObjFunc, nil)
175
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
178
reader.refreshEbsExtents()
179
assert.Equal(t, reader.valid, tc.expectValid)
183
func TestPrepareEbsSlice(t *testing.T) {
184
testCase := []struct {
185
getObjFunc func(*meta.MetaWrapper, uint64) (uint64, uint64, []proto.ExtentKey, []proto.ObjExtentKey, error)
190
{nil, -1, 100, syscall.EIO},
191
{MockGetObjExtentsTrue, 0, 100, nil},
192
{MockGetObjExtentsTrue, 501, 100, io.EOF},
193
{MockGetObjExtentsTrue, 400, 101, nil},
194
{MockGetObjExtentsFalse, 0, 100, syscall.EIO},
195
{MockGetObjExtentsFalse, 501, 100, syscall.EIO},
196
{MockGetObjExtentsFalse, 400, 101, syscall.EIO},
199
for _, tc := range testCase {
200
mw := &meta.MetaWrapper{}
201
err := gohook.HookMethod(mw, "GetObjExtents", tc.getObjFunc, nil)
203
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
206
reader.limitManager = manager.NewLimitManager(nil)
208
_, got := reader.prepareEbsSlice(tc.offset, tc.size)
209
assert.Equal(t, tc.expectError, got)
213
func TestRead(t *testing.T) {
214
testCase := []struct {
217
getObjFunc func(*meta.MetaWrapper, uint64) (uint64, uint64, []proto.ExtentKey, []proto.ObjExtentKey, error)
218
bcacheGetFunc func(*bcache.BcacheClient, string, []byte, uint64, uint32) (int, error)
219
checkDpExistFunc func(*stream.ExtentClient, uint64) error
220
readExtentFunc func(*stream.ExtentClient, uint64, *proto.ExtentKey, []byte, int, int) (int, error, bool)
221
ebsReadFunc func(*BlobStoreClient, context.Context, string, []byte, uint64, uint64, proto.ObjExtentKey) (int, error)
224
{true, 2, MockGetObjExtentsTrue, MockGetTrue, MockCheckDataPartitionExistTrue, MockReadExtentTrue, MockEbscReadTrue, os.ErrInvalid},
225
{false, 2, MockGetObjExtentsFalse, MockGetTrue, MockCheckDataPartitionExistTrue, MockReadExtentTrue, MockEbscReadTrue, syscall.EIO},
226
{false, 2, MockGetObjExtentsTrue, MockGetTrue, MockCheckDataPartitionExistTrue, MockReadExtentTrue, MockEbscReadFalse, syscall.EIO},
227
{false, 2, MockGetObjExtentsTrue, MockGetTrue, MockCheckDataPartitionExistTrue, MockReadExtentTrue, MockEbscReadTrue, nil},
230
for _, tc := range testCase {
232
reader.limitManager = manager.NewLimitManager(nil)
233
reader.close = tc.close
234
reader.readConcurrency = tc.readConcurrency
236
mw := &meta.MetaWrapper{}
237
ebsc := &BlobStoreClient{}
238
bc := &bcache.BcacheClient{}
239
ec := &stream.ExtentClient{}
240
err := gohook.HookMethod(mw, "GetObjExtents", tc.getObjFunc, nil)
242
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
244
err = gohook.HookMethod(ec, "CheckDataPartitionExsit", tc.checkDpExistFunc, nil)
246
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
248
err = gohook.HookMethod(ec, "ReadExtent", tc.readExtentFunc, nil)
250
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
252
err = gohook.HookMethod(ebsc, "Read", tc.ebsReadFunc, nil)
254
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
256
err = gohook.HookMethod(bc, "Get", tc.bcacheGetFunc, nil)
258
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
265
ctx := context.Background()
266
buf := make([]byte, 500)
267
_, gotError := reader.Read(ctx, buf, 0, 100)
268
assert.Equal(t, tc.expectError, gotError)
272
func TestAsyncCache(t *testing.T) {
273
ebsc := &BlobStoreClient{}
274
ec := &stream.ExtentClient{}
275
bc := &bcache.BcacheClient{}
276
err := gohook.HookMethod(ec, "Write", MockWriteTrue, nil)
278
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
280
err = gohook.HookMethod(bc, "Put", MockPutTrue, nil)
282
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
285
testCase := []struct {
286
ebsReadFunc func(*BlobStoreClient, context.Context, string, []byte, uint64, uint64, proto.ObjExtentKey) (int, error)
291
{MockEbscReadTrue, proto.NoCache, true, rand.Uint64() % 1000},
292
{MockEbscReadTrue, proto.RCache, true, rand.Uint64() % 1000},
293
{MockEbscReadTrue, proto.RWCache, true, rand.Uint64() % 1000},
294
{MockEbscReadTrue, proto.NoCache, false, rand.Uint64() % 1000},
295
{MockEbscReadTrue, proto.RCache, false, rand.Uint64() % 1000},
296
{MockEbscReadTrue, proto.RWCache, false, rand.Uint64() % 1000},
297
{MockEbscReadFalse, proto.NoCache, true, rand.Uint64() % 1000},
298
{MockEbscReadFalse, proto.RCache, true, rand.Uint64() % 1000},
299
{MockEbscReadFalse, proto.RWCache, true, rand.Uint64() % 1000},
300
{MockEbscReadFalse, proto.NoCache, false, rand.Uint64() % 1000},
301
{MockEbscReadFalse, proto.RCache, false, rand.Uint64() % 1000},
302
{MockEbscReadFalse, proto.RWCache, false, rand.Uint64() % 1000},
305
size := rand.Intn(1000)
306
objEk := proto.ObjExtentKey{
317
for _, tc := range testCase {
319
reader.limitManager = manager.NewLimitManager(nil)
320
reader.cacheThreshold = 1000
321
ctx := context.Background()
322
err := gohook.HookMethod(ebsc, "Read", tc.ebsReadFunc, nil)
324
t.Fatal(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
326
reader.fileLength = tc.fileSize
327
reader.cacheAction = tc.cacheAction
328
reader.asyncCache(ctx, "cacheKey", objEk)
332
func TestNeedCacheL2(t *testing.T) {
333
testCase := []struct {
340
{proto.NoCache, 10, 100, false, false},
341
{proto.NoCache, 10, 100, true, true},
342
{proto.NoCache, 101, 100, true, true},
343
{proto.NoCache, 101, 100, false, false},
344
{proto.RCache, 10, 100, false, true},
345
{proto.RCache, 10, 100, true, true},
346
{proto.RCache, 101, 100, false, false},
347
{proto.RCache, 101, 100, true, true},
348
{proto.RWCache, 10, 100, false, true},
349
{proto.RWCache, 10, 100, true, true},
350
{proto.RWCache, 101, 100, false, false},
351
{proto.RWCache, 101, 100, true, true},
354
for _, tc := range testCase {
356
reader.limitManager = manager.NewLimitManager(nil)
357
reader.cacheAction = tc.cacheAction
358
reader.fileLength = tc.fileLength
359
reader.cacheThreshold = tc.cacheThreshold
360
reader.fileCache = tc.fileCache
361
got := reader.needCacheL2()
362
assert.Equal(t, tc.expectCache, got)
366
func TestNeedCacheL1(t *testing.T) {
367
testCase := []struct {
375
for _, tc := range testCase {
377
reader.limitManager = manager.NewLimitManager(nil)
378
reader.enableBcache = tc.enableCache
379
got := reader.needCacheL1()
380
assert.Equal(t, tc.expectCache, got)
384
func TestReadSliceRange(t *testing.T) {
385
testCase := []struct {
387
extentKey proto.ExtentKey
388
bcacheGetFunc func(*bcache.BcacheClient, string, []byte, uint64, uint32) (int, error)
389
checkDpExistFunc func(*stream.ExtentClient, uint64) error
390
readExtentFunc func(*stream.ExtentClient, uint64, *proto.ExtentKey, []byte, int, int) (int, error, bool)
391
ebsReadFunc func(*BlobStoreClient, context.Context, string, []byte, uint64, uint64, proto.ObjExtentKey) (int, error)
397
MockGetTrue, MockCheckDataPartitionExistTrue,
398
MockReadExtentTrue, MockEbscReadTrue, nil,
403
MockGetTrue, MockCheckDataPartitionExistTrue,
404
MockReadExtentTrue, MockEbscReadFalse, syscall.EIO,
409
MockGetTrue, MockCheckDataPartitionExistTrue,
410
MockReadExtentTrue, MockEbscReadFalse, nil,
415
MockGetFalse, MockCheckDataPartitionExistTrue,
416
MockReadExtentTrue, MockEbscReadFalse, syscall.EIO,
421
MockGetFalse, MockCheckDataPartitionExistTrue,
422
MockReadExtentTrue, MockEbscReadTrue, nil,
426
for _, tc := range testCase {
428
reader.limitManager = manager.NewLimitManager(nil)
429
ebsc := &BlobStoreClient{}
430
bc := &bcache.BcacheClient{}
431
ec := &stream.ExtentClient{}
432
reader.volName = "cfs"
434
reader.fileLength = 10
435
reader.cacheThreshold = 100
436
reader.err = make(chan error)
438
rs.rSize = uint32(len("Hello world"))
439
rs.Data = make([]byte, len("Hello world"))
440
rs.extentKey = tc.extentKey
442
reader.enableBcache = tc.enableBcache
443
err := gohook.HookMethod(ebsc, "Read", tc.ebsReadFunc, nil)
445
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
447
err = gohook.HookMethod(bc, "Get", tc.bcacheGetFunc, nil)
449
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
451
err = gohook.HookMethod(ec, "CheckDataPartitionExsit", tc.checkDpExistFunc, nil)
453
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
455
err = gohook.HookMethod(ec, "ReadExtent", tc.readExtentFunc, nil)
457
panic(fmt.Sprintf("Hook advance instance method failed:%s", err.Error()))
463
ctx := context.Background()
468
gotError := reader.readSliceRange(ctx, rs)
469
assert.Equal(t, tc.expectError, gotError)
473
func MockGetObjExtentsTrue(m *meta.MetaWrapper, inode uint64) (gen uint64, size uint64,
474
extents []proto.ExtentKey, objExtents []proto.ObjExtentKey, err error) {
475
objEks := make([]proto.ObjExtentKey, 0)
477
expectedFileSize := 0
478
for i := 0; i < objEkLen; i++ {
480
objEks = append(objEks, proto.ObjExtentKey{Size: uint64(100), FileOffset: uint64(expectedFileSize)})
481
expectedFileSize += size
483
return 1, 1, nil, objEks, nil
486
func MockGetObjExtentsFalse(m *meta.MetaWrapper, inode uint64) (gen uint64, size uint64,
487
extents []proto.ExtentKey, objExtents []proto.ObjExtentKey, err error) {
488
return 1, 1, nil, nil, errors.New("Get objEks failed")
491
func MockEbscReadTrue(ebsc *BlobStoreClient, ctx context.Context, volName string,
492
buf []byte, offset uint64, size uint64,
493
oek proto.ObjExtentKey) (readN int, err error) {
494
reader := strings.NewReader("Hello world.")
495
readN, err = io.ReadFull(reader, buf)
499
func MockEbscReadFalse(ebsc *BlobStoreClient, ctx context.Context, volName string,
500
buf []byte, offset uint64, size uint64,
501
oek proto.ObjExtentKey) (readN int, err error) {
502
return 0, syscall.EIO
505
func MockReadExtentTrue(client *stream.ExtentClient, inode uint64, ek *proto.ExtentKey,
506
data []byte, offset int, size int) (read int, err error, b bool) {
507
return len("Hello world"), nil, true
510
func MockReadExtentFalse(client *stream.ExtentClient, inode uint64, ek *proto.ExtentKey,
511
data []byte, offset int, size int) (read int, err error) {
512
return 0, errors.New("Read extent failed")
515
func MockCheckDataPartitionExistTrue(client *stream.ExtentClient, partitionID uint64) error {
519
func MockCheckDataPartitionExistFalse(client *stream.ExtentClient, partitionID uint64) error {
520
return errors.New("CheckDataPartitionExist failed")
523
func MockWriteTrue(client *stream.ExtentClient, inode uint64, offset int, data []byte,
524
flags int, checkFunc func() error) (write int, err error) {
525
return len(data), nil
528
func MockWriteFalse(client *stream.ExtentClient, inode uint64, offset int, data []byte,
529
flags int) (write int, err error) {
530
return 0, errors.New("Write failed")
533
func MockPutTrue(bc *bcache.BcacheClient, key string, buf []byte) error {
537
func MockPutFalse(bc *bcache.BcacheClient, key string, buf []byte) error {
538
return errors.New("Bcache put failed")
541
func MockGetTrue(bc *bcache.BcacheClient, key string, buf []byte, offset uint64, size uint32) (int, error) {
542
return int(size), nil
545
func MockGetFalse(bc *bcache.BcacheClient, key string, buf []byte, offset uint64, size uint32) (int, error) {
546
return 0, errors.New("Bcache get failed")