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

     1  package rosedb
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  )
     9  
    10  func TestRoseDB_LPush(t *testing.T) {
    11  	t.Run("fileio", func(t *testing.T) {
    12  		testRoseDBPush(t, true, FileIO, KeyOnlyMemMode)
    13  	})
    14  
    15  	t.Run("mmap", func(t *testing.T) {
    16  		testRoseDBPush(t, true, MMap, KeyValueMemMode)
    17  	})
    18  }
    19  
    20  func TestRoseDB_RPush(t *testing.T) {
    21  	t.Run("fileio", func(t *testing.T) {
    22  		testRoseDBPush(t, false, FileIO, KeyOnlyMemMode)
    23  	})
    24  
    25  	t.Run("mmap", func(t *testing.T) {
    26  		testRoseDBPush(t, false, MMap, KeyValueMemMode)
    27  	})
    28  }
    29  
    30  func TestRoseDB_Push_UntilRotateFile(t *testing.T) {
    31  	path := filepath.Join("/tmp", "rosedb")
    32  	opts := DefaultOptions(path)
    33  	opts.LogFileSizeThreshold = 32 << 20
    34  	db, err := Open(opts)
    35  	assert.Nil(t, err)
    36  	defer destroyDB(db)
    37  
    38  	writeCount := 600000
    39  	key := []byte("mylist")
    40  	for i := 0; i <= writeCount; i++ {
    41  		err := db.LPush(key, GetValue128B())
    42  		assert.Nil(t, err)
    43  	}
    44  }
    45  
    46  func testRoseDBPush(t *testing.T, isLush bool, ioType IOType, mode DataIndexMode) {
    47  	path := filepath.Join("/tmp", "rosedb")
    48  	opts := DefaultOptions(path)
    49  	opts.IoType = ioType
    50  	opts.IndexMode = mode
    51  	db, err := Open(opts)
    52  	assert.Nil(t, err)
    53  	defer destroyDB(db)
    54  
    55  	type args struct {
    56  		key    []byte
    57  		values [][]byte
    58  	}
    59  	tests := []struct {
    60  		name    string
    61  		db      *RoseDB
    62  		args    args
    63  		wantErr bool
    64  	}{
    65  		{
    66  			"nil-value", db, args{key: GetKey(0), values: [][]byte{GetValue16B()}}, false,
    67  		},
    68  		{
    69  			"one-value", db, args{key: GetKey(1), values: [][]byte{GetValue16B()}}, false,
    70  		},
    71  		{
    72  			"multi-value", db, args{key: GetKey(2), values: [][]byte{GetValue16B(), GetValue16B(), GetValue16B()}}, false,
    73  		},
    74  	}
    75  	for _, tt := range tests {
    76  		t.Run(tt.name, func(t *testing.T) {
    77  			if isLush {
    78  				if err := tt.db.LPush(tt.args.key, tt.args.values...); (err != nil) != tt.wantErr {
    79  					t.Errorf("LPush() error = %v, wantErr %v", err, tt.wantErr)
    80  				}
    81  			} else {
    82  				if err := tt.db.RPush(tt.args.key, tt.args.values...); (err != nil) != tt.wantErr {
    83  					t.Errorf("RPush() error = %v, wantErr %v", err, tt.wantErr)
    84  				}
    85  			}
    86  		})
    87  	}
    88  }
    89  
    90  func TestRoseDB_LPop(t *testing.T) {
    91  	t.Run("fileio", func(t *testing.T) {
    92  		testRoseDBLPop(t, FileIO, KeyOnlyMemMode)
    93  	})
    94  	t.Run("mmap", func(t *testing.T) {
    95  		testRoseDBLPop(t, MMap, KeyValueMemMode)
    96  	})
    97  }
    98  
    99  func TestRoseDB_RPop(t *testing.T) {
   100  	t.Run("fileio", func(t *testing.T) {
   101  		testRoseDBRPop(t, FileIO, KeyOnlyMemMode)
   102  	})
   103  	t.Run("mmap", func(t *testing.T) {
   104  		testRoseDBRPop(t, MMap, KeyValueMemMode)
   105  	})
   106  }
   107  
   108  func testRoseDBLPop(t *testing.T, ioType IOType, mode DataIndexMode) {
   109  	path := filepath.Join("/tmp", "rosedb")
   110  	opts := DefaultOptions(path)
   111  	opts.IoType = ioType
   112  	opts.IndexMode = mode
   113  	db, err := Open(opts)
   114  	assert.Nil(t, err)
   115  	defer destroyDB(db)
   116  
   117  	// none
   118  	listKey := []byte("my_list")
   119  	pop, err := db.LPop(listKey)
   120  	assert.Nil(t, pop)
   121  	assert.Nil(t, err)
   122  
   123  	// one
   124  	err = db.LPush(listKey, GetValue16B())
   125  	assert.Nil(t, err)
   126  	v1, err := db.LPop(listKey)
   127  	assert.Nil(t, err)
   128  	assert.NotNil(t, v1)
   129  
   130  	// rpush one
   131  	err = db.RPush(listKey, GetValue16B())
   132  	assert.Nil(t, err)
   133  	v2, err := db.LPop(listKey)
   134  	assert.Nil(t, err)
   135  	assert.NotNil(t, v2)
   136  
   137  	//	multi
   138  	err = db.LPush(listKey, GetKey(0), GetKey(1), GetKey(2))
   139  	assert.Nil(t, err)
   140  
   141  	var values [][]byte
   142  	for db.LLen(listKey) > 0 {
   143  		v, err := db.LPop(listKey)
   144  		assert.Nil(t, err)
   145  		values = append(values, v)
   146  	}
   147  	expected := [][]byte{GetKey(2), GetKey(1), GetKey(0)}
   148  	assert.Equal(t, expected, values)
   149  
   150  	// lRange
   151  	values, err = db.LRange(listKey, 0, -1)
   152  	assert.Equal(t, ErrKeyNotFound, err)
   153  	assert.Nil(t, values)
   154  }
   155  
   156  func testRoseDBRPop(t *testing.T, ioType IOType, mode DataIndexMode) {
   157  	path := filepath.Join("/tmp", "rosedb")
   158  	opts := DefaultOptions(path)
   159  	opts.IoType = ioType
   160  	opts.IndexMode = mode
   161  	db, err := Open(opts)
   162  	assert.Nil(t, err)
   163  	defer destroyDB(db)
   164  
   165  	// none
   166  	listKey := []byte("my_list")
   167  	pop, err := db.RPop(listKey)
   168  	assert.Nil(t, pop)
   169  	assert.Nil(t, err)
   170  
   171  	// one
   172  	err = db.RPush(listKey, GetValue16B())
   173  	assert.Nil(t, err)
   174  	v1, err := db.RPop(listKey)
   175  	assert.Nil(t, err)
   176  	assert.NotNil(t, v1)
   177  
   178  	// lpush one
   179  	err = db.LPush(listKey, GetValue16B())
   180  	assert.Nil(t, err)
   181  	v2, err := db.RPop(listKey)
   182  	assert.Nil(t, err)
   183  	assert.NotNil(t, v2)
   184  
   185  	//	multi
   186  	err = db.RPush(listKey, GetKey(0), GetKey(1), GetKey(2))
   187  	assert.Nil(t, err)
   188  
   189  	var values [][]byte
   190  	for db.LLen(listKey) > 0 {
   191  		v, err := db.RPop(listKey)
   192  		assert.Nil(t, err)
   193  		values = append(values, v)
   194  	}
   195  	expected := [][]byte{GetKey(2), GetKey(1), GetKey(0)}
   196  	assert.Equal(t, expected, values)
   197  
   198  	// lRange
   199  	values, err = db.LRange(listKey, 0, -1)
   200  	assert.Equal(t, ErrKeyNotFound, err)
   201  	assert.Nil(t, values)
   202  }
   203  
   204  func TestRoseDB_LMove(t *testing.T) {
   205  	t.Run("fileio", func(t *testing.T) {
   206  		testRoseDBLMove(t, FileIO, KeyOnlyMemMode)
   207  	})
   208  	t.Run("mmap", func(t *testing.T) {
   209  		testRoseDBLMove(t, MMap, KeyValueMemMode)
   210  	})
   211  }
   212  
   213  func testRoseDBLMove(t *testing.T, ioType IOType, mode DataIndexMode) {
   214  	path := filepath.Join("/tmp", "rosedb")
   215  	opts := DefaultOptions(path)
   216  	opts.IoType = ioType
   217  	opts.IndexMode = mode
   218  	db, err := Open(opts)
   219  	assert.Nil(t, err)
   220  	defer destroyDB(db)
   221  
   222  	// none
   223  	srcListKey := []byte("src_list")
   224  	dstListKey := []byte("dst_list")
   225  	v, err := db.LMove(srcListKey, dstListKey, true, true)
   226  	assert.Nil(t, v)
   227  	assert.Nil(t, err)
   228  
   229  	err = db.RPush(srcListKey, GetKey(1), GetKey(2), GetKey(3), GetKey(4), GetKey(5))
   230  	assert.Nil(t, err)
   231  
   232  	// left-pop left-push
   233  	v, err = db.LMove(srcListKey, dstListKey, true, true)
   234  	assert.Nil(t, err)
   235  	assert.Equal(t, v, GetKey(1))
   236  	// src[2, 3, 4, 5]	dst[1]
   237  
   238  	// left-pop right-push
   239  	v, err = db.LMove(srcListKey, dstListKey, true, false)
   240  	assert.Nil(t, err)
   241  	assert.Equal(t, v, GetKey(2))
   242  	// src[3, 4, 5]		dst[1, 2]
   243  
   244  	// right-pop left-push
   245  	v, err = db.LMove(srcListKey, dstListKey, false, true)
   246  	assert.Nil(t, err)
   247  	assert.Equal(t, v, GetKey(5))
   248  	// src[3, 4]		dst[5, 1, 2]
   249  
   250  	// right-pop right-push
   251  	v, err = db.LMove(srcListKey, dstListKey, false, false)
   252  	assert.Nil(t, err)
   253  	assert.Equal(t, v, GetKey(4))
   254  	// src[3]		dst[5, 1, 2, 4]
   255  
   256  	v, err = db.LIndex(dstListKey, 0)
   257  	assert.Nil(t, err)
   258  	assert.Equal(t, v, GetKey(5))
   259  
   260  	v, err = db.LIndex(dstListKey, 1)
   261  	assert.Nil(t, err)
   262  	assert.Equal(t, v, GetKey(1))
   263  
   264  	v, err = db.LIndex(dstListKey, 2)
   265  	assert.Nil(t, err)
   266  	assert.Equal(t, v, GetKey(2))
   267  
   268  	v, err = db.LIndex(dstListKey, 3)
   269  	assert.Nil(t, err)
   270  	assert.Equal(t, v, GetKey(4))
   271  }
   272  
   273  func TestRoseDB_LLen(t *testing.T) {
   274  	path := filepath.Join("/tmp", "rosedb")
   275  	opts := DefaultOptions(path)
   276  	db, err := Open(opts)
   277  	assert.Nil(t, err)
   278  	defer destroyDB(db)
   279  
   280  	listKey := []byte("my_list")
   281  	err = db.LPush(listKey, GetValue16B(), GetValue16B(), GetValue16B())
   282  	assert.Nil(t, err)
   283  	assert.Equal(t, 3, db.LLen(listKey))
   284  
   285  	// close and reopen
   286  	err = db.Close()
   287  	assert.Nil(t, err)
   288  
   289  	db2, err := Open(opts)
   290  	assert.Nil(t, err)
   291  	defer func() {
   292  		_ = db2.Close()
   293  	}()
   294  	err = db2.LPush(listKey, GetValue16B(), GetValue16B(), GetValue16B())
   295  	assert.Nil(t, err)
   296  	assert.Equal(t, 6, db2.LLen(listKey))
   297  }
   298  
   299  func TestRoseDB_DiscardStat_List(t *testing.T) {
   300  	helper := func(isDelete bool) {
   301  		path := filepath.Join("/tmp", "rosedb")
   302  		opts := DefaultOptions(path)
   303  		opts.LogFileSizeThreshold = 64 << 20
   304  		db, err := Open(opts)
   305  		assert.Nil(t, err)
   306  		defer destroyDB(db)
   307  
   308  		listKey := []byte("my_list")
   309  		writeCount := 800000
   310  		for i := 0; i < writeCount; i++ {
   311  			err := db.LPush(listKey, GetKey(i))
   312  			assert.Nil(t, err)
   313  		}
   314  
   315  		for i := 0; i < writeCount/3; i++ {
   316  			if i%2 == 0 {
   317  				_, err := db.LPop(listKey)
   318  				assert.Nil(t, err)
   319  			} else {
   320  				_, err := db.RPop(listKey)
   321  				assert.Nil(t, err)
   322  			}
   323  		}
   324  
   325  		_ = db.Sync()
   326  		ccl, err := db.discards[List].getCCL(10, 0.2)
   327  		t.Log(err)
   328  		t.Log(ccl)
   329  		assert.Nil(t, err)
   330  		assert.Equal(t, 1, len(ccl))
   331  	}
   332  
   333  	t.Run("delete", func(t *testing.T) {
   334  		helper(true)
   335  	})
   336  }
   337  
   338  func TestRoseDB_ListGC(t *testing.T) {
   339  	path := filepath.Join("/tmp", "rosedb")
   340  	opts := DefaultOptions(path)
   341  	opts.LogFileSizeThreshold = 64 << 20
   342  	db, err := Open(opts)
   343  	assert.Nil(t, err)
   344  	defer destroyDB(db)
   345  
   346  	listKey := []byte("my_list")
   347  	writeCount := 800000
   348  	for i := 0; i < writeCount; i++ {
   349  		err := db.LPush(listKey, GetKey(i))
   350  		assert.Nil(t, err)
   351  	}
   352  
   353  	for i := 0; i < writeCount/3; i++ {
   354  		if i%2 == 0 {
   355  			_, err := db.LPop(listKey)
   356  			assert.Nil(t, err)
   357  		} else {
   358  			_, err := db.RPop(listKey)
   359  			assert.Nil(t, err)
   360  		}
   361  	}
   362  
   363  	l1 := db.LLen(listKey)
   364  	assert.Equal(t, writeCount-writeCount/3, l1)
   365  
   366  	err = db.RunLogFileGC(List, 0, 0.3)
   367  	assert.Nil(t, err)
   368  
   369  	l2 := db.LLen(listKey)
   370  	assert.Equal(t, writeCount-writeCount/3, l2)
   371  }
   372  
   373  func TestRoseDB_LPushX(t *testing.T) {
   374  	t.Run("fileio", func(t *testing.T) {
   375  		testRoseDBPushX(t, true, FileIO, KeyOnlyMemMode)
   376  	})
   377  
   378  	t.Run("mmap", func(t *testing.T) {
   379  		testRoseDBPushX(t, true, MMap, KeyValueMemMode)
   380  	})
   381  }
   382  
   383  func TestRoseDB_RPushX(t *testing.T) {
   384  	t.Run("fileio", func(t *testing.T) {
   385  		testRoseDBPushX(t, false, FileIO, KeyOnlyMemMode)
   386  	})
   387  
   388  	t.Run("mmap", func(t *testing.T) {
   389  		testRoseDBPushX(t, false, MMap, KeyValueMemMode)
   390  	})
   391  }
   392  
   393  func testRoseDBPushX(t *testing.T, isLPush bool, ioType IOType, mode DataIndexMode) {
   394  	path := filepath.Join("/tmp", "rosedb")
   395  	opts := DefaultOptions(path)
   396  	opts.IoType = ioType
   397  	opts.IndexMode = mode
   398  	db, err := Open(opts)
   399  	assert.Nil(t, err)
   400  	defer destroyDB(db)
   401  
   402  	err = db.LPush(GetKey(1), []byte("1"))
   403  	assert.Nil(t, err)
   404  	err = db.LPush(GetKey(2), []byte("1"))
   405  	assert.Nil(t, err)
   406  
   407  	type args struct {
   408  		key    []byte
   409  		values [][]byte
   410  	}
   411  	tests := []struct {
   412  		name    string
   413  		db      *RoseDB
   414  		args    args
   415  		wantErr bool
   416  	}{
   417  		{
   418  			"nil-key", db, args{key: GetKey(0), values: [][]byte{GetValue16B()}}, true,
   419  		},
   420  		{
   421  			"one-value", db, args{key: GetKey(1), values: [][]byte{GetValue16B()}}, false,
   422  		},
   423  		{
   424  			"multi-value", db, args{key: GetKey(2), values: [][]byte{GetValue16B(), GetValue16B(), GetValue16B()}}, false,
   425  		},
   426  	}
   427  	for _, tt := range tests {
   428  		t.Run(tt.name, func(t *testing.T) {
   429  			if isLPush {
   430  				if err := tt.db.LPushX(tt.args.key, tt.args.values...); (err != nil) != tt.wantErr {
   431  					t.Errorf("LPushX() error = %v, wantErr %v", err, tt.wantErr)
   432  				}
   433  			} else {
   434  				if err := tt.db.RPushX(tt.args.key, tt.args.values...); (err != nil) != tt.wantErr {
   435  					t.Errorf("RPushX() error = %v, wantErr %v", err, tt.wantErr)
   436  				}
   437  			}
   438  		})
   439  	}
   440  }
   441  func TestRoseDB_LIndex(t *testing.T) {
   442  	t.Run("fileio", func(t *testing.T) {
   443  		testRoseDBRLIndex(t, FileIO, KeyOnlyMemMode)
   444  	})
   445  	t.Run("mmap", func(t *testing.T) {
   446  		testRoseDBRLIndex(t, MMap, KeyValueMemMode)
   447  	})
   448  }
   449  
   450  func testRoseDBRLIndex(t *testing.T, ioType IOType, mode DataIndexMode) {
   451  	path := filepath.Join("/tmp", "rosedb")
   452  	opts := DefaultOptions(path)
   453  	opts.IoType = ioType
   454  	opts.IndexMode = mode
   455  	db, err := Open(opts)
   456  	assert.Nil(t, err)
   457  	defer destroyDB(db)
   458  
   459  	// none
   460  	listKey := []byte("my_list")
   461  	v, err := db.LIndex(listKey, 0)
   462  	assert.Nil(t, v)
   463  	assert.Nil(t, err)
   464  
   465  	// one
   466  	err = db.RPush(listKey, GetKey(1))
   467  	assert.Nil(t, err)
   468  
   469  	lVal1, err := db.LIndex(listKey, 0)
   470  	assert.Nil(t, err)
   471  	assert.Equal(t, lVal1, GetKey(1))
   472  
   473  	rVal1, err := db.LIndex(listKey, -1)
   474  	assert.Nil(t, err)
   475  	assert.Equal(t, rVal1, GetKey(1))
   476  
   477  	// out of right range with one
   478  	rOut1, err := db.LIndex(listKey, 1)
   479  	assert.Equal(t, ErrWrongIndex, err)
   480  	assert.Nil(t, rOut1)
   481  
   482  	// out of left range with one
   483  	lOut1, err := db.LIndex(listKey, -2)
   484  	assert.Equal(t, ErrWrongIndex, err)
   485  	assert.Nil(t, lOut1)
   486  
   487  	// two
   488  	err = db.RPush(listKey, GetKey(2))
   489  	assert.Nil(t, err)
   490  
   491  	lVal1, err = db.LIndex(listKey, 0)
   492  	assert.Nil(t, err)
   493  	assert.Equal(t, lVal1, GetKey(1))
   494  
   495  	lVal2, err := db.LIndex(listKey, 1)
   496  	assert.Nil(t, err)
   497  	assert.Equal(t, lVal2, GetKey(2))
   498  
   499  	rVal1, err = db.LIndex(listKey, -2)
   500  	assert.Nil(t, err)
   501  	assert.Equal(t, rVal1, GetKey(1))
   502  
   503  	rVal2, err := db.LIndex(listKey, -1)
   504  	assert.Nil(t, err)
   505  	assert.Equal(t, rVal2, GetKey(2))
   506  
   507  	// out of right range with two
   508  	rOut2, err := db.LIndex(listKey, 2)
   509  	assert.Equal(t, ErrWrongIndex, err)
   510  	assert.Nil(t, rOut2)
   511  
   512  	// out of left range with two
   513  	lOut2, err := db.LIndex(listKey, -3)
   514  	assert.Equal(t, ErrWrongIndex, err)
   515  	assert.Nil(t, lOut2)
   516  }
   517  
   518  func TestRoseDB_LSet(t *testing.T) {
   519  	t.Run("fileio", func(t *testing.T) {
   520  		testRoseDBLSet(t, FileIO, KeyOnlyMemMode)
   521  	})
   522  	t.Run("mmap", func(t *testing.T) {
   523  		testRoseDBLSet(t, MMap, KeyValueMemMode)
   524  	})
   525  }
   526  
   527  func testRoseDBLSet(t *testing.T, ioType IOType, mode DataIndexMode) {
   528  	path := filepath.Join("/tmp", "rosedb")
   529  	opts := DefaultOptions(path)
   530  	opts.IoType = ioType
   531  	opts.IndexMode = mode
   532  	db, err := Open(opts)
   533  	assert.Nil(t, err)
   534  	defer destroyDB(db)
   535  
   536  	// none
   537  	listKey := []byte("my_list")
   538  	err = db.LSet(listKey, 0, GetKey(1))
   539  	assert.Equal(t, err, ErrKeyNotFound)
   540  
   541  	// one
   542  	err = db.RPush(listKey, GetKey(1))
   543  	assert.Nil(t, err)
   544  	err = db.LSet(listKey, 0, GetKey(111))
   545  	assert.Nil(t, err)
   546  	lPop, err := db.LPop(listKey)
   547  	assert.Nil(t, err)
   548  	assert.Equal(t, GetKey(111), lPop)
   549  
   550  	// three
   551  	err = db.RPush(listKey, GetKey(1))
   552  	assert.Nil(t, err)
   553  	err = db.RPush(listKey, GetKey(2))
   554  	assert.Nil(t, err)
   555  	err = db.RPush(listKey, GetKey(3))
   556  	assert.Nil(t, err)
   557  	err = db.LSet(listKey, 0, GetKey(111))
   558  	assert.Nil(t, err)
   559  	err = db.LSet(listKey, 1, GetKey(222))
   560  	assert.Nil(t, err)
   561  	err = db.LSet(listKey, -1, GetKey(333))
   562  	assert.Nil(t, err)
   563  	lPop, err = db.LPop(listKey)
   564  	assert.Nil(t, err)
   565  	assert.Equal(t, GetKey(111), lPop)
   566  	lPop, err = db.LPop(listKey)
   567  	assert.Nil(t, err)
   568  	assert.Equal(t, GetKey(222), lPop)
   569  	lPop, err = db.LPop(listKey)
   570  	assert.Nil(t, err)
   571  	assert.Equal(t, GetKey(333), lPop)
   572  
   573  	// out of range
   574  	err = db.RPush(listKey, GetKey(1))
   575  	assert.Nil(t, err)
   576  	err = db.LSet(listKey, 1, GetKey(111))
   577  	assert.Equal(t, err, ErrWrongIndex)
   578  	err = db.LSet(listKey, -2, GetKey(111))
   579  	assert.Equal(t, err, ErrWrongIndex)
   580  }
   581  
   582  func TestRoseDB_listSequence(t *testing.T) {
   583  
   584  	t.Run("fileio", func(t *testing.T) {
   585  		testListSequence(t, FileIO, KeyOnlyMemMode)
   586  	})
   587  
   588  	t.Run("mmap", func(t *testing.T) {
   589  		testListSequence(t, MMap, KeyValueMemMode)
   590  	})
   591  }
   592  
   593  func testListSequence(t *testing.T, ioType IOType, mode DataIndexMode) {
   594  	path := filepath.Join("/tmp", "rosedb")
   595  	opts := DefaultOptions(path)
   596  	opts.IoType = ioType
   597  	opts.IndexMode = mode
   598  	db, err := Open(opts)
   599  	assert.Nil(t, err)
   600  	defer destroyDB(db)
   601  
   602  	listKey := []byte("my_list")
   603  	// prepare List
   604  	err = db.LPush(listKey, []byte("zero"))
   605  	assert.Nil(t, err)
   606  	err = db.LPush(listKey, []byte("negative one"))
   607  	assert.Nil(t, err)
   608  	err = db.RPush(listKey, []byte("one"))
   609  	assert.Nil(t, err)
   610  	err = db.RPush(listKey, []byte("two"))
   611  	assert.Nil(t, err)
   612  	err = db.RPush(listKey, []byte("three"))
   613  	assert.Nil(t, err)
   614  
   615  	type args struct {
   616  		key   []byte
   617  		index int
   618  	}
   619  
   620  	tests := []struct {
   621  		name     string
   622  		db       *RoseDB
   623  		args     args
   624  		expected uint32
   625  		wantErr  bool
   626  	}{
   627  		{
   628  			"0", db, args{key: listKey, index: 0}, uint32(initialListSeq - 1), false,
   629  		},
   630  		{
   631  			"negative-1", db, args{key: listKey, index: -3}, uint32(initialListSeq + 1), false,
   632  		},
   633  		{
   634  			"negative-2", db, args{key: listKey, index: -4}, uint32(initialListSeq), false,
   635  		},
   636  		{
   637  			"positive-1", db, args{key: listKey, index: 1}, uint32(initialListSeq), false,
   638  		},
   639  		{
   640  			"positive-2", db, args{key: listKey, index: 3}, uint32(initialListSeq + 2), false,
   641  		},
   642  	}
   643  
   644  	for _, tt := range tests {
   645  		t.Run(tt.name, func(t *testing.T) {
   646  			idxTree := db.listIndex.trees[string(tt.args.key)]
   647  			start, end, err := db.listMeta(idxTree, tt.args.key)
   648  			assert.Nil(t, err)
   649  			actual, err := tt.db.listSequence(start, end, tt.args.index)
   650  			assert.Equal(t, tt.expected, actual, "expected is not the same with actual")
   651  			if (err != nil) != tt.wantErr {
   652  				t.Errorf("convertLogicalIndexToSeq() error = %v, wantErr %v", err, tt.wantErr)
   653  			}
   654  		})
   655  	}
   656  }
   657  
   658  func TestRoseDB_LRange(t *testing.T) {
   659  	t.Run("fileio", func(t *testing.T) {
   660  		testRoseDBLRange(t, FileIO, KeyOnlyMemMode)
   661  	})
   662  
   663  	t.Run("mmap", func(t *testing.T) {
   664  		testRoseDBLRange(t, MMap, KeyValueMemMode)
   665  	})
   666  }
   667  
   668  func testRoseDBLRange(t *testing.T, ioType IOType, mode DataIndexMode) {
   669  	path := filepath.Join("/tmp", "rosedb")
   670  	opts := DefaultOptions(path)
   671  	opts.IoType = ioType
   672  	opts.IndexMode = mode
   673  	db, err := Open(opts)
   674  	assert.Nil(t, err)
   675  	defer destroyDB(db)
   676  
   677  	type args struct {
   678  		key   []byte
   679  		start int
   680  		end   int
   681  	}
   682  
   683  	listKey := []byte("my_list")
   684  	// prepare List
   685  	err = db.LPush(listKey, []byte("zero"))
   686  	assert.Nil(t, err)
   687  	err = db.LPush(listKey, []byte("negative one"))
   688  	assert.Nil(t, err)
   689  	err = db.RPush(listKey, []byte("one"))
   690  	assert.Nil(t, err)
   691  	err = db.RPush(listKey, []byte("two"))
   692  	assert.Nil(t, err)
   693  	err = db.RPush(listKey, []byte("three"))
   694  	assert.Nil(t, err)
   695  
   696  	tests := []struct {
   697  		name       string
   698  		db         *RoseDB
   699  		args       args
   700  		wantValues [][]byte
   701  		wantErr    bool
   702  	}{
   703  		{
   704  			"nil-key", db, args{key: nil, start: 0, end: 3}, [][]byte(nil), true,
   705  		},
   706  		{
   707  			"start==end", db, args{key: listKey, start: 1, end: 1}, [][]byte{[]byte("zero")}, false,
   708  		},
   709  		{
   710  			"start==end==tailSeq", db, args{key: listKey, start: 4, end: 4}, [][]byte{[]byte("three")}, false,
   711  		},
   712  		{
   713  			"end reset to endSeq", db, args{key: listKey, start: 0, end: 8},
   714  			[][]byte{[]byte("negative one"), []byte("zero"), []byte("one"), []byte("two"), []byte("three")}, false,
   715  		},
   716  		{
   717  			"start and end reset", db, args{key: listKey, start: -100, end: 100},
   718  			[][]byte{[]byte("negative one"), []byte("zero"), []byte("one"), []byte("two"), []byte("three")}, false,
   719  		},
   720  		{
   721  			"start negative end positive", db, args{key: listKey, start: -4, end: 2},
   722  			[][]byte{[]byte("zero"), []byte("one")}, false,
   723  		},
   724  		{
   725  			"start out of range", db, args{key: listKey, start: 5, end: 10}, [][]byte(nil), true,
   726  		},
   727  		{
   728  			"end out of range", db, args{key: listKey, start: 1, end: -8}, [][]byte(nil), true,
   729  		},
   730  		{
   731  			"end larger than start", db, args{key: listKey, start: -1, end: 1}, [][]byte(nil), true,
   732  		},
   733  	}
   734  
   735  	for _, tt := range tests {
   736  		t.Run(tt.name, func(t *testing.T) {
   737  			actual, actualErr := tt.db.LRange(tt.args.key, tt.args.start, tt.args.end)
   738  			assert.Equal(t, tt.wantValues, actual, "actual is not the same with expected")
   739  			if (actualErr != nil) != tt.wantErr {
   740  				t.Errorf("LRange() error = %v, wantErr %v", actualErr, tt.wantErr)
   741  			}
   742  		})
   743  	}
   744  }
   745  
   746  func TestRoseDB_LRem(t *testing.T) {
   747  	t.Run("fileio", func(t *testing.T) {
   748  		testRoseDBLRem(t, FileIO, KeyOnlyMemMode)
   749  	})
   750  	t.Run("mmap", func(t *testing.T) {
   751  		testRoseDBLRem(t, MMap, KeyValueMemMode)
   752  	})
   753  }
   754  
   755  func testRoseDBLRem(t *testing.T, ioType IOType, mode DataIndexMode) {
   756  	path := filepath.Join("/tmp", "rosedb")
   757  	opts := DefaultOptions(path)
   758  	opts.IoType = ioType
   759  	opts.IndexMode = mode
   760  	db, err := Open(opts)
   761  	assert.Nil(t, err)
   762  	defer destroyDB(db)
   763  
   764  	listKey := []byte("my_list")
   765  	v, err := db.LRem(listKey, 1, GetKey(1))
   766  	assert.Equal(t, 0, v)
   767  	assert.Nil(t, err)
   768  	v, err = db.LRem(listKey, 0, GetKey(1))
   769  	assert.Equal(t, 0, v)
   770  	assert.Nil(t, err)
   771  	v, err = db.LRem(listKey, -1, GetKey(1))
   772  	assert.Equal(t, 0, v)
   773  	assert.Nil(t, err)
   774  
   775  	err = db.RPush(listKey, GetKey(1), GetKey(2), GetKey(1), GetKey(3), GetKey(3), GetKey(4))
   776  	assert.Nil(t, err)
   777  	// list : 1 2 1 3 3 4
   778  	expected := [][]byte{GetKey(1), GetKey(2), GetKey(1), GetKey(3), GetKey(3), GetKey(4)}
   779  	v, err = db.LRem(listKey, 1, GetKey(5))
   780  	assert.Equal(t, 0, v)
   781  	assert.Nil(t, err)
   782  	values, err := db.LRange(listKey, 0, -1)
   783  	assert.Equal(t, expected, values)
   784  	assert.Nil(t, err)
   785  
   786  	// list : 1 2 1 3 3 4
   787  	expected = [][]byte{GetKey(1), GetKey(2), GetKey(1), GetKey(3), GetKey(3), GetKey(4)}
   788  	v, err = db.LRem(listKey, 0, GetKey(5))
   789  	assert.Equal(t, 0, v)
   790  	assert.Nil(t, err)
   791  	values, err = db.LRange(listKey, 0, -1)
   792  	assert.Equal(t, expected, values)
   793  	assert.Nil(t, err)
   794  
   795  	// list : 1 2 1 3 3 4
   796  	expected = [][]byte{GetKey(1), GetKey(2), GetKey(1), GetKey(3), GetKey(3), GetKey(4)}
   797  	v, err = db.LRem(listKey, -1, GetKey(5))
   798  	assert.Equal(t, 0, v)
   799  	assert.Nil(t, err)
   800  	values, err = db.LRange(listKey, 0, -1)
   801  	assert.Equal(t, expected, values)
   802  	assert.Nil(t, err)
   803  
   804  	// list : 1 2 1 3 3 4
   805  	expected = [][]byte{GetKey(2), GetKey(3), GetKey(3), GetKey(4)}
   806  	v, err = db.LRem(listKey, 3, GetKey(1))
   807  	assert.Equal(t, 2, v)
   808  	assert.Nil(t, err)
   809  	values, err = db.LRange(listKey, 0, -1)
   810  	assert.Equal(t, expected, values)
   811  	assert.Nil(t, err)
   812  
   813  	// list : 2 3 3 4
   814  	expected = [][]byte{GetKey(2), GetKey(4)}
   815  	v, err = db.LRem(listKey, -3, GetKey(3))
   816  	assert.Equal(t, 2, v)
   817  	assert.Nil(t, err)
   818  	values, err = db.LRange(listKey, 0, -1)
   819  	assert.Equal(t, expected, values)
   820  	assert.Nil(t, err)
   821  
   822  	// list : 2 4
   823  	expected = [][]byte{GetKey(4)}
   824  	v, err = db.LRem(listKey, 0, GetKey(2))
   825  	assert.Equal(t, 1, v)
   826  	assert.Nil(t, err)
   827  	values, err = db.LRange(listKey, 0, -1)
   828  	assert.Equal(t, expected, values)
   829  	assert.Nil(t, err)
   830  
   831  	// list : 4
   832  	err = db.RPush(listKey, GetKey(3), GetKey(2), GetKey(1))
   833  	assert.Nil(t, err)
   834  
   835  	// list : 4 3 2 1
   836  	expected = [][]byte{GetKey(3), GetKey(2), GetKey(1)}
   837  	v, err = db.LRem(listKey, 1, GetKey(4))
   838  	assert.Equal(t, 1, v)
   839  	assert.Nil(t, err)
   840  	values, err = db.LRange(listKey, 0, -1)
   841  	assert.Equal(t, expected, values)
   842  	assert.Nil(t, err)
   843  
   844  	// list : 3 2 1
   845  	expected = [][]byte{GetKey(3), GetKey(2)}
   846  	v, err = db.LRem(listKey, -1, GetKey(1))
   847  	assert.Equal(t, 1, v)
   848  	assert.Nil(t, err)
   849  	values, err = db.LRange(listKey, 0, -1)
   850  	assert.Equal(t, expected, values)
   851  	assert.Nil(t, err)
   852  
   853  	// list : 3 2
   854  	expected = [][]byte{GetKey(3)}
   855  	v, err = db.LRem(listKey, 0, GetKey(2))
   856  	assert.Equal(t, 1, v)
   857  	assert.Nil(t, err)
   858  	values, err = db.LRange(listKey, 0, -1)
   859  	assert.Equal(t, expected, values)
   860  	assert.Nil(t, err)
   861  }