github.com/jimmyx0x/go-ethereum@v1.10.28/common/lru/blob_lru_test.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package lru
    18  
    19  import (
    20  	"encoding/binary"
    21  	"fmt"
    22  	"testing"
    23  )
    24  
    25  type testKey [8]byte
    26  
    27  func mkKey(i int) (key testKey) {
    28  	binary.LittleEndian.PutUint64(key[:], uint64(i))
    29  	return key
    30  }
    31  
    32  func TestSizeConstrainedCache(t *testing.T) {
    33  	lru := NewSizeConstrainedCache[testKey, []byte](100)
    34  	var want uint64
    35  	// Add 11 items of 10 byte each. First item should be swapped out
    36  	for i := 0; i < 11; i++ {
    37  		k := mkKey(i)
    38  		v := fmt.Sprintf("value-%04d", i)
    39  		lru.Add(k, []byte(v))
    40  		want += uint64(len(v))
    41  		if want > 100 {
    42  			want = 100
    43  		}
    44  		if have := lru.size; have != want {
    45  			t.Fatalf("size wrong, have %d want %d", have, want)
    46  		}
    47  	}
    48  	// Zero:th should be evicted
    49  	{
    50  		k := mkKey(0)
    51  		if _, ok := lru.Get(k); ok {
    52  			t.Fatalf("should be evicted: %v", k)
    53  		}
    54  	}
    55  	// Elems 1-11 should be present
    56  	for i := 1; i < 11; i++ {
    57  		k := mkKey(i)
    58  		want := fmt.Sprintf("value-%04d", i)
    59  		have, ok := lru.Get(k)
    60  		if !ok {
    61  			t.Fatalf("missing key %v", k)
    62  		}
    63  		if string(have) != want {
    64  			t.Fatalf("wrong value, have %v want %v", have, want)
    65  		}
    66  	}
    67  }
    68  
    69  // This test adds inserting an element exceeding the max size.
    70  func TestSizeConstrainedCacheOverflow(t *testing.T) {
    71  	lru := NewSizeConstrainedCache[testKey, []byte](100)
    72  
    73  	// Add 10 items of 10 byte each, filling the cache
    74  	for i := 0; i < 10; i++ {
    75  		k := mkKey(i)
    76  		v := fmt.Sprintf("value-%04d", i)
    77  		lru.Add(k, []byte(v))
    78  	}
    79  	// Add one single large elem. We expect it to swap out all entries.
    80  	{
    81  		k := mkKey(1337)
    82  		v := make([]byte, 200)
    83  		lru.Add(k, v)
    84  	}
    85  	// Elems 0-9 should be missing
    86  	for i := 1; i < 10; i++ {
    87  		k := mkKey(i)
    88  		if _, ok := lru.Get(k); ok {
    89  			t.Fatalf("should be evicted: %v", k)
    90  		}
    91  	}
    92  	// The size should be accurate
    93  	if have, want := lru.size, uint64(200); have != want {
    94  		t.Fatalf("size wrong, have %d want %d", have, want)
    95  	}
    96  	// Adding one small item should swap out the large one
    97  	{
    98  		i := 0
    99  		k := mkKey(i)
   100  		v := fmt.Sprintf("value-%04d", i)
   101  		lru.Add(k, []byte(v))
   102  		if have, want := lru.size, uint64(10); have != want {
   103  			t.Fatalf("size wrong, have %d want %d", have, want)
   104  		}
   105  	}
   106  }
   107  
   108  // This checks what happens when inserting the same k/v multiple times.
   109  func TestSizeConstrainedCacheSameItem(t *testing.T) {
   110  	lru := NewSizeConstrainedCache[testKey, []byte](100)
   111  
   112  	// Add one 10 byte-item 10 times.
   113  	k := mkKey(0)
   114  	v := fmt.Sprintf("value-%04d", 0)
   115  	for i := 0; i < 10; i++ {
   116  		lru.Add(k, []byte(v))
   117  	}
   118  
   119  	// The size should be accurate.
   120  	if have, want := lru.size, uint64(10); have != want {
   121  		t.Fatalf("size wrong, have %d want %d", have, want)
   122  	}
   123  }
   124  
   125  // This tests that empty/nil values are handled correctly.
   126  func TestSizeConstrainedCacheEmpties(t *testing.T) {
   127  	lru := NewSizeConstrainedCache[testKey, []byte](100)
   128  
   129  	// This test abuses the lru a bit, using different keys for identical value(s).
   130  	for i := 0; i < 10; i++ {
   131  		lru.Add(testKey{byte(i)}, []byte{})
   132  		lru.Add(testKey{byte(255 - i)}, nil)
   133  	}
   134  
   135  	// The size should not count, only the values count. So this could be a DoS
   136  	// since it basically has no cap, and it is intentionally overloaded with
   137  	// different-keyed 0-length values.
   138  	if have, want := lru.size, uint64(0); have != want {
   139  		t.Fatalf("size wrong, have %d want %d", have, want)
   140  	}
   141  
   142  	for i := 0; i < 10; i++ {
   143  		if v, ok := lru.Get(testKey{byte(i)}); !ok {
   144  			t.Fatalf("test %d: expected presence", i)
   145  		} else if v == nil {
   146  			t.Fatalf("test %d, v is nil", i)
   147  		}
   148  
   149  		if v, ok := lru.Get(testKey{byte(255 - i)}); !ok {
   150  			t.Fatalf("test %d: expected presence", i)
   151  		} else if v != nil {
   152  			t.Fatalf("test %d, v is not nil", i)
   153  		}
   154  	}
   155  }