github.com/nutsdb/nutsdb@v1.0.4/list_test.go (about)

     1  // Copyright 2023 The PromiseDB Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file expect in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package nutsdb
    15  
    16  import (
    17  	"bytes"
    18  	"math/rand"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func ListPush(t *testing.T, list *List, key string, r *Record, isLeft bool, expectError error) {
    26  	var e error
    27  	if isLeft {
    28  		e = list.LPush(key, r)
    29  	} else {
    30  		e = list.RPush(key, r)
    31  	}
    32  	assertErr(t, e, expectError)
    33  }
    34  
    35  func ListPop(t *testing.T, list *List, key string, isLeft bool, expectVal *Record, expectError error) {
    36  	var (
    37  		e error
    38  		r *Record
    39  	)
    40  
    41  	if isLeft {
    42  		r, e = list.LPop(key)
    43  	} else {
    44  		r, e = list.RPop(key)
    45  	}
    46  	if expectError != nil {
    47  		require.Equal(t, expectError, e)
    48  	} else {
    49  		require.NoError(t, e)
    50  		require.Equal(t, expectVal, r)
    51  	}
    52  }
    53  
    54  func ListCmp(t *testing.T, list *List, key string, expectRecords []*Record, isReverse bool) {
    55  	records, err := list.LRange(key, 0, -1)
    56  	require.NoError(t, err)
    57  
    58  	if isReverse {
    59  		for i := len(expectRecords) - 1; i >= 0; i-- {
    60  			require.Equal(t, expectRecords[i], records[len(expectRecords)-1-i])
    61  		}
    62  	} else {
    63  		for i := 0; i < len(expectRecords); i++ {
    64  			require.Equal(t, expectRecords[i], records[i])
    65  		}
    66  	}
    67  }
    68  
    69  func TestList_LPush(t *testing.T) {
    70  	list := NewList()
    71  	// 测试 LPush
    72  	key := string(GetTestBytes(0))
    73  	expectRecords := generateRecords(5)
    74  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
    75  
    76  	for i := 0; i < len(expectRecords); i++ {
    77  		seq := generateSeq(&seqInfo, true)
    78  		newKey := encodeListKey([]byte(key), seq)
    79  		expectRecords[i].Key = newKey
    80  		ListPush(t, list, string(newKey), expectRecords[i], true, nil)
    81  	}
    82  
    83  	ListCmp(t, list, key, expectRecords, true)
    84  }
    85  
    86  func TestList_RPush(t *testing.T) {
    87  	list := NewList()
    88  	expectRecords := generateRecords(5)
    89  	key := string(GetTestBytes(0))
    90  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
    91  
    92  	for i := 0; i < len(expectRecords); i++ {
    93  		seq := generateSeq(&seqInfo, false)
    94  		newKey := encodeListKey([]byte(key), seq)
    95  		expectRecords[i].Key = newKey
    96  		ListPush(t, list, string(newKey), expectRecords[i], false, nil)
    97  	}
    98  
    99  	ListCmp(t, list, key, expectRecords, false)
   100  }
   101  
   102  func TestList_Pop(t *testing.T) {
   103  	list := NewList()
   104  	expectRecords := generateRecords(5)
   105  	key := string(GetTestBytes(0))
   106  
   107  	ListPop(t, list, key, true, nil, ErrListNotFound)
   108  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
   109  
   110  	for i := 0; i < len(expectRecords); i++ {
   111  		seq := generateSeq(&seqInfo, false)
   112  		newKey := encodeListKey([]byte(key), seq)
   113  		expectRecords[i].Key = newKey
   114  		ListPush(t, list, string(newKey), expectRecords[i], false, nil)
   115  	}
   116  
   117  	ListPop(t, list, key, true, expectRecords[0], nil)
   118  	expectRecords = expectRecords[1:]
   119  
   120  	ListPop(t, list, key, false, expectRecords[len(expectRecords)-1], nil)
   121  	expectRecords = expectRecords[:len(expectRecords)-1]
   122  
   123  	ListCmp(t, list, key, expectRecords, false)
   124  }
   125  
   126  func TestList_LRem(t *testing.T) {
   127  	list := NewList()
   128  	records := generateRecords(2)
   129  	key := string(GetTestBytes(0))
   130  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
   131  
   132  	for i := 0; i < 3; i++ {
   133  		seq := generateSeq(&seqInfo, false)
   134  		newKey := encodeListKey([]byte(key), seq)
   135  		records[0].Key = newKey
   136  		ListPush(t, list, string(newKey), records[0], false, nil)
   137  	}
   138  
   139  	seq := generateSeq(&seqInfo, false)
   140  	newKey := encodeListKey([]byte(key), seq)
   141  	records[1].Key = newKey
   142  	ListPush(t, list, string(newKey), records[1], false, nil)
   143  
   144  	seq = generateSeq(&seqInfo, false)
   145  	newKey = encodeListKey([]byte(key), seq)
   146  	records[0].Key = newKey
   147  	ListPush(t, list, string(newKey), records[0], false, nil)
   148  
   149  	seq = generateSeq(&seqInfo, false)
   150  	newKey = encodeListKey([]byte(key), seq)
   151  	records[1].Key = newKey
   152  	ListPush(t, list, string(newKey), records[1], false, nil)
   153  
   154  	// r1 r1 r1 r2 r1 r2
   155  	expectRecords := []*Record{records[0], records[0], records[0], records[1], records[0], records[1]}
   156  
   157  	cmp := func(r *Record) (bool, error) {
   158  		return bytes.Equal(r.Value, records[0].Value), nil
   159  	}
   160  
   161  	// r1 r1 r1 r2 r2
   162  	err := list.LRem(key, -1, cmp)
   163  	require.NoError(t, err)
   164  	expectRecords = append(expectRecords[0:4], expectRecords[5:]...)
   165  	ListCmp(t, list, key, expectRecords, false)
   166  
   167  	// r1 r2 r2
   168  	err = list.LRem(key, 2, cmp)
   169  	require.NoError(t, err)
   170  	expectRecords = expectRecords[2:]
   171  	ListCmp(t, list, key, expectRecords, false)
   172  
   173  	cmp = func(r *Record) (bool, error) {
   174  		return bytes.Equal(r.Value, records[1].Value), nil
   175  	}
   176  
   177  	// r1
   178  	err = list.LRem(key, 0, cmp)
   179  	require.NoError(t, err)
   180  	expectRecords = expectRecords[0:1]
   181  	ListCmp(t, list, key, expectRecords, false)
   182  }
   183  
   184  func TestList_LTrim(t *testing.T) {
   185  	list := NewList()
   186  	expectRecords := generateRecords(5)
   187  	key := string(GetTestBytes(0))
   188  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
   189  
   190  	for i := 0; i < len(expectRecords); i++ {
   191  		seq := generateSeq(&seqInfo, false)
   192  		newKey := encodeListKey([]byte(key), seq)
   193  		expectRecords[i].Key = newKey
   194  		ListPush(t, list, string(newKey), expectRecords[i], false, nil)
   195  	}
   196  
   197  	err := list.LTrim(key, 1, 3)
   198  	require.NoError(t, err)
   199  	expectRecords = expectRecords[1 : len(expectRecords)-1]
   200  	ListCmp(t, list, key, expectRecords, false)
   201  }
   202  
   203  func TestList_LRemByIndex(t *testing.T) {
   204  	list := NewList()
   205  	expectRecords := generateRecords(8)
   206  	key := string(GetTestBytes(0))
   207  	seqInfo := HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
   208  
   209  	// r1 r2 r3 r4 r5 r6 r7 r8
   210  	for i := 0; i < 8; i++ {
   211  		seq := generateSeq(&seqInfo, false)
   212  		newKey := encodeListKey([]byte(key), seq)
   213  		expectRecords[i].Key = newKey
   214  		ListPush(t, list, string(newKey), expectRecords[i], false, nil)
   215  	}
   216  
   217  	// r1 r2 r4 r5 r6 r7 r8
   218  	err := list.LRemByIndex(key, []int{2})
   219  	require.NoError(t, err)
   220  	expectRecords = append(expectRecords[0:2], expectRecords[3:]...)
   221  	ListCmp(t, list, key, expectRecords, false)
   222  
   223  	// r2 r6 r7 r8
   224  	err = list.LRemByIndex(key, []int{0, 2, 3})
   225  	require.NoError(t, err)
   226  	expectRecords = expectRecords[1:]
   227  	expectRecords = append(expectRecords[0:1], expectRecords[3:]...)
   228  	ListCmp(t, list, key, expectRecords, false)
   229  
   230  	err = list.LRemByIndex(key, []int{0, 0, 0})
   231  	require.NoError(t, err)
   232  	expectRecords = expectRecords[1:]
   233  	ListCmp(t, list, key, expectRecords, false)
   234  }
   235  
   236  func generateRecords(count int) []*Record {
   237  	rand.Seed(time.Now().UnixNano())
   238  	records := make([]*Record, count)
   239  	for i := 0; i < count; i++ {
   240  		key := GetTestBytes(i)
   241  		val := GetRandomBytes(24)
   242  
   243  		record := &Record{
   244  			Key:       key,
   245  			Value:     val,
   246  			FileID:    int64(i),
   247  			DataPos:   uint64(rand.Uint32()),
   248  			ValueSize: uint32(len(val)),
   249  			Timestamp: uint64(time.Now().Unix()),
   250  			TTL:       uint32(rand.Intn(3600)),
   251  			TxID:      uint64(rand.Intn(1000)),
   252  		}
   253  		records[i] = record
   254  	}
   255  	return records
   256  }