github.com/flower-corp/rosedb@v1.1.2-0.20230117132829-21dc4f7b319a/ioselector/io_selector_test.go (about)

     1  package ioselector
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/stretchr/testify/assert"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  )
    10  
    11  func TestNewFileIOSelector(t *testing.T) {
    12  	testNewIOSelector(t, 0)
    13  }
    14  
    15  func TestNewMMapSelector(t *testing.T) {
    16  	testNewIOSelector(t, 1)
    17  }
    18  
    19  func TestFileIOSelector_Write(t *testing.T) {
    20  	testIOSelectorWrite(t, 0)
    21  }
    22  
    23  func TestMMapSelector_Write(t *testing.T) {
    24  	testIOSelectorWrite(t, 1)
    25  }
    26  
    27  func TestFileIOSelector_Read(t *testing.T) {
    28  	testIOSelectorRead(t, 0)
    29  }
    30  
    31  func TestMMapSelector_Read(t *testing.T) {
    32  	testIOSelectorRead(t, 1)
    33  }
    34  
    35  func TestFileIOSelector_Sync(t *testing.T) {
    36  	testIOSelectorSync(t, 0)
    37  }
    38  
    39  func TestMMapSelector_Sync(t *testing.T) {
    40  	testIOSelectorSync(t, 1)
    41  }
    42  
    43  func TestFileIOSelector_Close(t *testing.T) {
    44  	testIOSelectorClose(t, 0)
    45  }
    46  
    47  func TestMMapSelector_Close(t *testing.T) {
    48  	testIOSelectorClose(t, 1)
    49  }
    50  
    51  func TestFileIOSelector_Delete(t *testing.T) {
    52  	testIOSelectorDelete(t, 0)
    53  }
    54  
    55  func TestMMapSelector_Delete(t *testing.T) {
    56  	testIOSelectorDelete(t, 1)
    57  }
    58  
    59  func testNewIOSelector(t *testing.T, ioType uint8) {
    60  	type args struct {
    61  		fName string
    62  		fsize int64
    63  	}
    64  	tests := []struct {
    65  		name string
    66  		args args
    67  	}{
    68  		{
    69  			"size-zero", args{fName: "000000001.wal", fsize: 0},
    70  		},
    71  		{
    72  			"size-negative", args{fName: "000000002.wal", fsize: -1},
    73  		},
    74  		{
    75  			"size-big", args{fName: "000000003.wal", fsize: 1024 << 20},
    76  		},
    77  	}
    78  	for _, tt := range tests {
    79  		t.Run(tt.name, func(t *testing.T) {
    80  			absPath, err := filepath.Abs(filepath.Join("/tmp", tt.args.fName))
    81  			assert.Nil(t, err)
    82  
    83  			var got IOSelector
    84  			if ioType == 0 {
    85  				got, err = NewFileIOSelector(absPath, tt.args.fsize)
    86  			}
    87  			if ioType == 1 {
    88  				got, err = NewMMapSelector(absPath, tt.args.fsize)
    89  			}
    90  			defer func() {
    91  				if got != nil {
    92  					err = got.Delete()
    93  					assert.Nil(t, err)
    94  				}
    95  			}()
    96  			if tt.args.fsize > 0 {
    97  				assert.Nil(t, err)
    98  				assert.NotNil(t, got)
    99  			} else {
   100  				assert.Equal(t, err, ErrInvalidFsize)
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func testIOSelectorWrite(t *testing.T, ioType uint8) {
   107  	absPath, err := filepath.Abs(filepath.Join("/tmp", "00000001.vlog"))
   108  	assert.Nil(t, err)
   109  	var size int64 = 1048576
   110  
   111  	var selector IOSelector
   112  	if ioType == 0 {
   113  		selector, err = NewFileIOSelector(absPath, size)
   114  	}
   115  	if ioType == 1 {
   116  		selector, err = NewMMapSelector(absPath, size)
   117  	}
   118  	assert.Nil(t, err)
   119  	defer func() {
   120  		if selector != nil {
   121  			_ = selector.Delete()
   122  		}
   123  	}()
   124  
   125  	type fields struct {
   126  		selector IOSelector
   127  	}
   128  	type args struct {
   129  		b      []byte
   130  		offset int64
   131  	}
   132  	tests := []struct {
   133  		name    string
   134  		fields  fields
   135  		args    args
   136  		want    int
   137  		wantErr bool
   138  	}{
   139  		{
   140  			"nil-byte", fields{selector: selector}, args{b: nil, offset: 0}, 0, false,
   141  		},
   142  		{
   143  			"one-byte", fields{selector: selector}, args{b: []byte("0"), offset: 0}, 1, false,
   144  		},
   145  		{
   146  			"many-bytes", fields{selector: selector}, args{b: []byte("lotusdb"), offset: 0}, 7, false,
   147  		},
   148  		{
   149  			"bigvalue-byte", fields{selector: selector}, args{b: []byte(fmt.Sprintf("%01048576d", 123)), offset: 0}, 1048576, false,
   150  		},
   151  		{
   152  			"exceed-size", fields{selector: selector}, args{b: []byte(fmt.Sprintf("%01048577d", 123)), offset: 0}, 1048577, false,
   153  		},
   154  		{
   155  			"EOF-error", fields{selector: selector}, args{b: []byte("lotusdb"), offset: -1}, 0, true,
   156  		},
   157  	}
   158  
   159  	for _, tt := range tests {
   160  		t.Run(tt.name, func(t *testing.T) {
   161  			got, err := tt.fields.selector.Write(tt.args.b, tt.args.offset)
   162  			// io.EOF err in mmmap.
   163  			if tt.want == 1048577 && ioType == 1 {
   164  				tt.wantErr = true
   165  				tt.want = 0
   166  			}
   167  			if (err != nil) != tt.wantErr {
   168  				t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
   169  				return
   170  			}
   171  			if got != tt.want {
   172  				t.Errorf("Write() got = %v, want %v", got, tt.want)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func testIOSelectorRead(t *testing.T, ioType uint8) {
   179  	absPath, err := filepath.Abs(filepath.Join("/tmp", "00000001.wal"))
   180  	var selector IOSelector
   181  	if ioType == 0 {
   182  		selector, err = NewFileIOSelector(absPath, 100)
   183  	}
   184  	if ioType == 1 {
   185  		selector, err = NewMMapSelector(absPath, 100)
   186  	}
   187  	assert.Nil(t, err)
   188  	defer func() {
   189  		if selector != nil {
   190  			_ = selector.Delete()
   191  		}
   192  	}()
   193  	offsets := writeSomeData(selector, t)
   194  	results := [][]byte{
   195  		[]byte(""),
   196  		[]byte("1"),
   197  		[]byte("lotusdb"),
   198  	}
   199  
   200  	type fields struct {
   201  		selector IOSelector
   202  	}
   203  	type args struct {
   204  		b      []byte
   205  		offset int64
   206  	}
   207  	tests := []struct {
   208  		name    string
   209  		fields  fields
   210  		args    args
   211  		want    int
   212  		wantErr bool
   213  	}{
   214  		{
   215  			"nil", fields{selector: selector}, args{b: make([]byte, 0), offset: offsets[0]}, 0, false,
   216  		},
   217  		{
   218  			"one-byte", fields{selector: selector}, args{b: make([]byte, 1), offset: offsets[1]}, 1, false,
   219  		},
   220  		{
   221  			"many-bytes", fields{selector: selector}, args{b: make([]byte, 7), offset: offsets[2]}, 7, false,
   222  		},
   223  		{
   224  			"EOF-1", fields{selector: selector}, args{b: make([]byte, 100), offset: -1}, 0, true,
   225  		},
   226  		{
   227  			"EOF-2", fields{selector: selector}, args{b: make([]byte, 100), offset: 1024}, 0, true,
   228  		},
   229  	}
   230  	for i, tt := range tests {
   231  		t.Run(tt.name, func(t *testing.T) {
   232  			got, err := tt.fields.selector.Read(tt.args.b, tt.args.offset)
   233  			if (err != nil) != tt.wantErr {
   234  				t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr)
   235  				return
   236  			}
   237  			if got != tt.want {
   238  				t.Errorf("Read() got = %v, want %v", got, tt.want)
   239  			}
   240  			if !tt.wantErr {
   241  				assert.Equal(t, tt.args.b, results[i])
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func writeSomeData(selector IOSelector, t *testing.T) []int64 {
   248  	tests := [][]byte{
   249  		[]byte(""),
   250  		[]byte("1"),
   251  		[]byte("lotusdb"),
   252  	}
   253  
   254  	var offsets []int64
   255  	var offset int64
   256  	for _, tt := range tests {
   257  		offsets = append(offsets, offset)
   258  		n, err := selector.Write(tt, offset)
   259  		assert.Nil(t, err)
   260  		offset += int64(n)
   261  	}
   262  	return offsets
   263  }
   264  
   265  func testIOSelectorSync(t *testing.T, ioType uint8) {
   266  	sync := func(id int, fsize int64) {
   267  		absPath, err := filepath.Abs(filepath.Join("/tmp", fmt.Sprintf("0000000%d.wal", id)))
   268  		assert.Nil(t, err)
   269  		var selector IOSelector
   270  		if ioType == 0 {
   271  			selector, err = NewFileIOSelector(absPath, fsize)
   272  		}
   273  		if ioType == 1 {
   274  			selector, err = NewMMapSelector(absPath, fsize)
   275  		}
   276  		assert.Nil(t, err)
   277  		defer func() {
   278  			if selector != nil {
   279  				_ = selector.Delete()
   280  			}
   281  		}()
   282  		writeSomeData(selector, t)
   283  		err = selector.Sync()
   284  		assert.Nil(t, err)
   285  	}
   286  
   287  	for i := 1; i < 4; i++ {
   288  		sync(i, int64(i*100))
   289  	}
   290  }
   291  
   292  func testIOSelectorClose(t *testing.T, ioType uint8) {
   293  	sync := func(id int, fsize int64) {
   294  		absPath, err := filepath.Abs(filepath.Join("/tmp", fmt.Sprintf("0000000%d.wal", id)))
   295  		defer func() {
   296  			_ = os.Remove(absPath)
   297  		}()
   298  		assert.Nil(t, err)
   299  		var selector IOSelector
   300  		if ioType == 0 {
   301  			selector, err = NewFileIOSelector(absPath, fsize)
   302  		}
   303  		if ioType == 1 {
   304  			selector, err = NewMMapSelector(absPath, fsize)
   305  		}
   306  		assert.Nil(t, err)
   307  		defer func() {
   308  			if selector != nil {
   309  				err := selector.Close()
   310  				assert.Nil(t, err)
   311  			}
   312  		}()
   313  		writeSomeData(selector, t)
   314  		assert.Nil(t, err)
   315  	}
   316  
   317  	for i := 1; i < 4; i++ {
   318  		sync(i, int64(i*100))
   319  	}
   320  }
   321  
   322  func testIOSelectorDelete(t *testing.T, ioType uint8) {
   323  	tests := []struct {
   324  		name    string
   325  		wantErr bool
   326  	}{
   327  		{"1", false},
   328  		{"2", false},
   329  		{"3", false},
   330  		{"4", false},
   331  	}
   332  	for i, tt := range tests {
   333  		t.Run(tt.name, func(t *testing.T) {
   334  			absPath, err := filepath.Abs(filepath.Join("/tmp", fmt.Sprintf("0000000%d.wal", i)))
   335  			assert.Nil(t, err)
   336  			var selector IOSelector
   337  			if ioType == 0 {
   338  				selector, err = NewFileIOSelector(absPath, int64((i+1)*100))
   339  			}
   340  			if ioType == 1 {
   341  				selector, err = NewFileIOSelector(absPath, int64((i+1)*100))
   342  			}
   343  			assert.Nil(t, err)
   344  
   345  			if err := selector.Delete(); (err != nil) != tt.wantErr {
   346  				t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
   347  			}
   348  		})
   349  	}
   350  }