github.com/searKing/golang/go@v1.2.117/exp/sync/lru_test.go (about)

     1  // Copyright 2022 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sync_test
     6  
     7  import (
     8  	"math/rand"
     9  	"runtime"
    10  	"slices"
    11  	"sync"
    12  	"testing"
    13  
    14  	sync_ "github.com/searKing/golang/go/exp/sync"
    15  )
    16  
    17  func TestLRU(t *testing.T) {
    18  	evictCounter := 0
    19  	onEvicted := func(k int, v int) {
    20  		if k != v {
    21  			t.Fatalf("Evict values not equal (%v!=%v)", k, v)
    22  		}
    23  		evictCounter++
    24  	}
    25  	l := sync_.NewLRU[int, int](128)
    26  	l.SetEvictCallback(onEvicted)
    27  
    28  	for i := 0; i < 256; i++ {
    29  		if i%2 == 0 {
    30  			l.Store(i, i)
    31  			continue
    32  		}
    33  		l.Add(i, i)
    34  	}
    35  	if l.Len() != 128 {
    36  		t.Fatalf("bad len: %v", l.Len())
    37  	}
    38  
    39  	if evictCounter != 128 {
    40  		t.Fatalf("bad evict count: %v", evictCounter)
    41  	}
    42  
    43  	for i, k := range l.Keys() {
    44  		if v, ok := l.Get(k); !ok || v != k || v != i+128 {
    45  			t.Fatalf("bad key: %v", k)
    46  		}
    47  	}
    48  	for i := 0; i < 128; i++ {
    49  		_, ok := l.Get(i)
    50  		if ok {
    51  			t.Fatalf("should be evicted")
    52  		}
    53  	}
    54  	for i := 128; i < 256; i++ {
    55  		_, ok := l.Get(i)
    56  		if !ok {
    57  			t.Fatalf("should not be evicted")
    58  		}
    59  	}
    60  	for i := 128; i < 192; i++ {
    61  		v, ok := l.LoadAndDelete(i)
    62  		if !ok {
    63  			t.Fatalf("should be contained")
    64  		}
    65  		if v != i {
    66  			t.Fatalf("bad key: %v", i)
    67  		}
    68  		ok = l.Remove(i)
    69  		if ok {
    70  			t.Fatalf("should not be contained")
    71  		}
    72  		_, ok = l.Get(i)
    73  		if ok {
    74  			t.Fatalf("should be deleted")
    75  		}
    76  	}
    77  
    78  	l.Get(192) // expect 192 to be last key in l.Keys()
    79  
    80  	for i, k := range l.Keys() {
    81  		if (i < 63 && k != i+193) || (i == 63 && k != 192) {
    82  			t.Fatalf("out of order key: %v", k)
    83  		}
    84  	}
    85  
    86  	l.Purge()
    87  	if l.Len() != 0 {
    88  		t.Fatalf("bad len: %v", l.Len())
    89  	}
    90  	if _, ok := l.Get(200); ok {
    91  		t.Fatalf("should contain nothing")
    92  	}
    93  }
    94  
    95  // Test that Resize can upsize and downsize
    96  func TestLRU_Resize(t *testing.T) {
    97  	onEvictCounter := 0
    98  	onEvicted := func(k int, v int) { onEvictCounter++ }
    99  
   100  	l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted)
   101  
   102  	// Downsize
   103  	l.Add(1, 1)
   104  	l.Add(2, 2)
   105  	evicted := l.Resize(1)
   106  	if evicted != 1 {
   107  		t.Errorf("1 element should have been evicted: %v", evicted)
   108  	}
   109  	if onEvictCounter != 1 {
   110  		t.Errorf("onEvicted should have been called 1 time: %v", onEvictCounter)
   111  	}
   112  
   113  	l.Add(3, 3)
   114  	if l.Contains(1) {
   115  		t.Errorf("Element 1 should have been evicted")
   116  	}
   117  
   118  	// Upsize
   119  	evicted = l.Resize(2)
   120  	if evicted != 0 {
   121  		t.Errorf("0 elements should have been evicted: %v", evicted)
   122  	}
   123  
   124  	l.Add(4, 4)
   125  	if !l.Contains(3) || !l.Contains(4) {
   126  		t.Errorf("Cache should have contained 2 elements")
   127  	}
   128  }
   129  
   130  // Test that Contains doesn't update recent-ness
   131  func TestLRU_Contains(t *testing.T) {
   132  	l := sync_.NewLRU[int, int](2)
   133  
   134  	l.Add(1, 1)
   135  	l.Add(2, 2)
   136  	if !l.Contains(1) {
   137  		t.Errorf("1 should be contained")
   138  	}
   139  
   140  	l.Add(3, 3)
   141  	if l.Contains(1) {
   142  		t.Errorf("Contains should not have updated recent-ness of 1")
   143  	}
   144  }
   145  
   146  // Test that Peek doesn't update recent-ness
   147  func TestLRU_Peek(t *testing.T) {
   148  	l := sync_.NewLRU[int, int](2)
   149  
   150  	l.Add(1, 1)
   151  	l.Add(2, 2)
   152  	if v, ok := l.Peek(1); !ok || v != 1 {
   153  		t.Errorf("1 should be set to 1: %v, %v", v, ok)
   154  	}
   155  
   156  	l.Add(3, 3)
   157  	if l.Contains(1) {
   158  		t.Errorf("should not have updated recent-ness of 1")
   159  	}
   160  }
   161  
   162  // Test that Add returns true/false if an eviction occurred
   163  func TestLRU_Add(t *testing.T) {
   164  	evictCounter := 0
   165  	onEvicted := func(k int, v int) { evictCounter++ }
   166  
   167  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   168  
   169  	if l.Add(1, 1) == true || evictCounter != 0 {
   170  		t.Errorf("should not have an eviction")
   171  	}
   172  	if l.Add(1, -1) == true || evictCounter != 0 {
   173  		t.Errorf("should not have an eviction")
   174  	}
   175  	if l.Add(2, 2) == false || evictCounter != 1 {
   176  		t.Errorf("should have an eviction")
   177  	}
   178  }
   179  
   180  func TestLRU_Store(t *testing.T) {
   181  	evictCounter := 0
   182  	onEvicted := func(k int, v int) { evictCounter++ }
   183  
   184  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   185  
   186  	l.Store(1, 1)
   187  	if l.Len() != 1 || evictCounter != 0 {
   188  		t.Errorf("should not have an eviction")
   189  	}
   190  	l.Store(1, -1)
   191  	if l.Len() != 1 || evictCounter != 0 {
   192  		t.Errorf("should not have an eviction")
   193  	}
   194  	l.Store(2, 2)
   195  	if l.Len() != 1 || evictCounter != 1 {
   196  		t.Errorf("should have an eviction")
   197  	}
   198  }
   199  
   200  func TestLRU_LoadOrStore(t *testing.T) {
   201  	evictCounter := 0
   202  	onEvicted := func(k int, v int) { evictCounter++ }
   203  
   204  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   205  
   206  	{
   207  		_, loaded := l.LoadOrStore(1, 1)
   208  		if loaded {
   209  			t.Errorf("should not loaded")
   210  		}
   211  		if evictCounter != 0 {
   212  			t.Errorf("should not have an eviction")
   213  		}
   214  	}
   215  	{
   216  		old, loaded := l.LoadOrStore(1, -1)
   217  		if !loaded {
   218  			t.Errorf("should loaded")
   219  		}
   220  		if old != 1 {
   221  			t.Errorf("should load old value 1")
   222  		}
   223  		if evictCounter != 0 {
   224  			t.Errorf("should not have an eviction")
   225  		}
   226  	}
   227  
   228  	{
   229  		_, loaded := l.LoadOrStore(2, 2)
   230  		if loaded {
   231  			t.Errorf("should not loaded")
   232  		}
   233  		if evictCounter != 1 {
   234  			t.Errorf("should not have an eviction")
   235  		}
   236  	}
   237  }
   238  
   239  func TestLRU_LoadAndDelete(t *testing.T) {
   240  	evictCounter := 0
   241  	onEvicted := func(k int, v int) { evictCounter++ }
   242  
   243  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   244  	l.Add(1, 1)
   245  	l.Add(2, 2)
   246  
   247  	{
   248  		_, loaded := l.LoadAndDelete(-1)
   249  		if loaded {
   250  			t.Errorf("should not loaded")
   251  		}
   252  	}
   253  	{
   254  		_, loaded := l.LoadAndDelete(1)
   255  		if loaded {
   256  			t.Errorf("should not loaded")
   257  		}
   258  	}
   259  
   260  	{
   261  		old, loaded := l.LoadAndDelete(2)
   262  		if !loaded {
   263  			t.Errorf("should not loaded")
   264  		}
   265  		if old != 2 {
   266  			t.Errorf("should load old value 2")
   267  		}
   268  	}
   269  }
   270  
   271  func TestLRU_Delete(t *testing.T) {
   272  	evictCounter := 0
   273  	onEvicted := func(k int, v int) { evictCounter++ }
   274  
   275  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   276  	l.Add(1, 1)
   277  	l.Add(2, 2)
   278  
   279  	{
   280  		l.Delete(-1)
   281  		if l.Len() != 1 {
   282  			t.Errorf("should not deleted")
   283  		}
   284  	}
   285  	{
   286  		l.Delete(1)
   287  		if l.Len() != 1 {
   288  			t.Errorf("should not deleted")
   289  		}
   290  	}
   291  	{
   292  		l.Delete(2)
   293  		if l.Len() != 0 {
   294  			t.Errorf("should not deleted")
   295  		}
   296  	}
   297  }
   298  
   299  func TestLRU_Remove(t *testing.T) {
   300  	evictCounter := 0
   301  	onEvicted := func(k int, v int) { evictCounter++ }
   302  
   303  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   304  	l.Add(1, 1)
   305  	l.Add(2, 2)
   306  
   307  	{
   308  		loaded := l.Remove(-1)
   309  		if loaded {
   310  			t.Errorf("should not loaded")
   311  		}
   312  	}
   313  	{
   314  		loaded := l.Remove(1)
   315  		if loaded {
   316  			t.Errorf("should not loaded")
   317  		}
   318  	}
   319  
   320  	{
   321  		loaded := l.Remove(2)
   322  		if !loaded {
   323  			t.Errorf("should not loaded")
   324  		}
   325  	}
   326  }
   327  
   328  func TestLRU_Swap(t *testing.T) {
   329  	evictCounter := 0
   330  	onEvicted := func(k int, v int) { evictCounter++ }
   331  
   332  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   333  
   334  	if _, loaded := l.Swap(1, 1); loaded == true || evictCounter != 0 {
   335  		t.Errorf("should not have an eviction")
   336  	}
   337  	if pre, loaded := l.Swap(1, -1); loaded == false || pre != 1 || evictCounter != 0 {
   338  		t.Errorf("should not have an eviction")
   339  	}
   340  	if _, loaded := l.Swap(2, 2); loaded == true || evictCounter != 1 {
   341  		t.Errorf("should have an eviction")
   342  	}
   343  }
   344  
   345  func TestLRU_CompareAndSwap(t *testing.T) {
   346  	evictCounter := 0
   347  	onEvicted := func(k int, v int) { evictCounter++ }
   348  
   349  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   350  	l.Add(1, 1)
   351  	l.Add(2, 2)
   352  
   353  	if swapped := l.CompareAndSwap(-1, -1, -2); swapped {
   354  		t.Errorf("should not swapped")
   355  	}
   356  	if swapped := l.CompareAndSwap(1, 1, -1); swapped {
   357  		t.Errorf("should not swapped")
   358  	}
   359  	if swapped := l.CompareAndSwap(2, -2, 2); swapped {
   360  		t.Errorf("should not swapped")
   361  	}
   362  	if swapped := l.CompareAndSwap(2, 2, -2); !swapped {
   363  		t.Errorf("should swapped")
   364  	} else {
   365  		if val, ok := l.Peek(2); !ok || val != -2 {
   366  			t.Errorf("should swapped with value -2")
   367  		}
   368  	}
   369  }
   370  
   371  func TestLRU_CompareAndDelete(t *testing.T) {
   372  	evictCounter := 0
   373  	onEvicted := func(k int, v int) { evictCounter++ }
   374  
   375  	l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted)
   376  	l.Add(1, 1)
   377  	l.Add(2, 2)
   378  
   379  	if deleted := l.CompareAndDelete(-1, -1); deleted {
   380  		t.Errorf("should not deleted")
   381  	}
   382  	if deleted := l.CompareAndDelete(1, 1); deleted {
   383  		t.Errorf("should not deleted")
   384  	}
   385  	if deleted := l.CompareAndDelete(2, -2); deleted {
   386  		t.Errorf("should not deleted")
   387  	}
   388  	if deleted := l.CompareAndDelete(2, 2); !deleted {
   389  		t.Errorf("should deleted")
   390  	} else {
   391  		if l.Len() != 0 {
   392  			t.Errorf("should empty")
   393  		}
   394  	}
   395  }
   396  
   397  func TestLRU_Keys(t *testing.T) {
   398  	evictCounter := 0
   399  	onEvicted := func(k int, v int) { evictCounter++ }
   400  
   401  	l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted)
   402  	l.Add(1, 1)
   403  	l.Add(2, 2)
   404  	l.Add(3, 3)
   405  
   406  	if !slices.Equal(l.Keys(), []int{2, 3}) {
   407  		t.Fatalf("bad key order: %v", l.Keys())
   408  	}
   409  }
   410  
   411  func TestLRU_Range(t *testing.T) {
   412  	evictCounter := 0
   413  	onEvicted := func(k int, v int) { evictCounter++ }
   414  
   415  	l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted)
   416  	l.Add(1, 1)
   417  	l.Add(2, 2)
   418  	l.Add(3, 3)
   419  
   420  	var keys, vals []int
   421  	l.Range(func(key int, value int) bool {
   422  		keys = append(keys, key)
   423  		vals = append(vals, value)
   424  		return true
   425  	})
   426  
   427  	if !slices.Equal(l.Keys(), keys) {
   428  		t.Fatalf("bad key order: %v", l.Keys())
   429  	}
   430  	if !slices.Equal(keys, vals) {
   431  		t.Fatalf("mismatched kv pairs: %v:%v", keys, vals)
   432  	}
   433  }
   434  
   435  func TestLRU_GetOldest(t *testing.T) {
   436  	l := sync_.NewLRU[int, int](128)
   437  
   438  	for i := 0; i < 256; i++ {
   439  		l.Add(i, i)
   440  	}
   441  	k, _, ok := l.GetOldest()
   442  	if !ok {
   443  		t.Fatalf("missing")
   444  	}
   445  	if k != 128 {
   446  		t.Fatalf("bad: %v", k)
   447  	}
   448  }
   449  
   450  func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
   451  	l := sync_.NewLRU[int, int](128)
   452  
   453  	for i := 0; i < 256; i++ {
   454  		l.Add(i, i)
   455  	}
   456  	k, _, ok := l.GetOldest()
   457  	if !ok {
   458  		t.Fatalf("missing")
   459  	}
   460  	if k != 128 {
   461  		t.Fatalf("bad: %v", k)
   462  	}
   463  
   464  	k, _, ok = l.RemoveOldest()
   465  	if !ok {
   466  		t.Fatalf("missing")
   467  	}
   468  	if k != 128 {
   469  		t.Fatalf("bad: %v", k)
   470  	}
   471  
   472  	k, _, ok = l.RemoveOldest()
   473  	if !ok {
   474  		t.Fatalf("missing")
   475  	}
   476  	if k != 129 {
   477  		t.Fatalf("bad: %v", k)
   478  	}
   479  }
   480  
   481  func TestLRU_PeekAndDeleteOldest(t *testing.T) {
   482  	l := sync_.NewLRU[int, int](128)
   483  
   484  	for i := 0; i < 256; i++ {
   485  		l.Add(i, i)
   486  	}
   487  	k, _, ok := l.PeekOldest()
   488  	if !ok {
   489  		t.Fatalf("missing")
   490  	}
   491  	if k != 128 {
   492  		t.Fatalf("bad: %v", k)
   493  	}
   494  
   495  	k, v, ok := l.PeekAndDeleteOldest()
   496  	if !ok {
   497  		t.Fatalf("missing")
   498  	}
   499  	if k != 128 {
   500  		t.Fatalf("bad key: %v", k)
   501  	}
   502  	if v != 128 {
   503  		t.Fatalf("bad value: %v", k)
   504  	}
   505  
   506  	k, _, ok = l.PeekAndDeleteOldest()
   507  	if !ok {
   508  		t.Fatalf("missing")
   509  	}
   510  	if k != 129 {
   511  		t.Fatalf("bad: %v", k)
   512  	}
   513  }
   514  
   515  func TestLRU_RemoveOldest(t *testing.T) {
   516  	l := sync_.NewLRU[int, int](128)
   517  
   518  	for i := 0; i < 256; i++ {
   519  		l.Add(i, i)
   520  	}
   521  	k, _, ok := l.GetOldest()
   522  	if !ok {
   523  		t.Fatalf("missing")
   524  	}
   525  	if k != 128 {
   526  		t.Fatalf("bad: %v", k)
   527  	}
   528  
   529  	k, _, ok = l.RemoveOldest()
   530  	if !ok {
   531  		t.Fatalf("missing")
   532  	}
   533  	if k != 128 {
   534  		t.Fatalf("bad: %v", k)
   535  	}
   536  
   537  	k, _, ok = l.RemoveOldest()
   538  	if !ok {
   539  		t.Fatalf("missing")
   540  	}
   541  	if k != 129 {
   542  		t.Fatalf("bad: %v", k)
   543  	}
   544  }
   545  
   546  func TestConcurrentRange(t *testing.T) {
   547  	const lruSize = 1 << 10
   548  
   549  	m := sync_.NewLRU[int64, int64](lruSize)
   550  	for n := int64(1); n <= lruSize; n++ {
   551  		m.Store(n, n)
   552  	}
   553  
   554  	done := make(chan struct{})
   555  	var wg sync.WaitGroup
   556  	defer func() {
   557  		close(done)
   558  		wg.Wait()
   559  	}()
   560  	for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- {
   561  		r := rand.New(rand.NewSource(g))
   562  		wg.Add(1)
   563  		go func(g int64) {
   564  			defer wg.Done()
   565  			for i := int64(0); ; i++ {
   566  				select {
   567  				case <-done:
   568  					return
   569  				default:
   570  				}
   571  				for n := int64(1); n < lruSize; n++ {
   572  					if r.Int63n(lruSize) == 0 {
   573  						m.Store(n, n*i*g)
   574  					} else {
   575  						m.Load(n)
   576  					}
   577  				}
   578  			}
   579  		}(g)
   580  	}
   581  
   582  	iters := 1 << 10
   583  	if testing.Short() {
   584  		iters = 16
   585  	}
   586  	for n := iters; n > 0; n-- {
   587  		seen := make(map[int64]bool, lruSize)
   588  
   589  		m.Range(func(ki, vi int64) bool {
   590  			k, v := ki, vi
   591  			if v%k != 0 {
   592  				t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v)
   593  			}
   594  			if seen[k] {
   595  				t.Fatalf("Range visited key %v twice", k)
   596  			}
   597  			seen[k] = true
   598  			return true
   599  		})
   600  
   601  		if len(seen) != lruSize {
   602  			t.Fatalf("Range visited %v elements of %v-element Map", len(seen), lruSize)
   603  		}
   604  	}
   605  }