golang.org/x/tools/gopls@v0.15.3/internal/util/lru/lru_test.go (about)

     1  // Copyright 2023 The Go Authors. 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 lru_test
     6  
     7  import (
     8  	"bytes"
     9  	cryptorand "crypto/rand"
    10  	"fmt"
    11  	"log"
    12  	mathrand "math/rand"
    13  	"strings"
    14  	"testing"
    15  
    16  	"golang.org/x/sync/errgroup"
    17  	"golang.org/x/tools/gopls/internal/util/lru"
    18  )
    19  
    20  func TestCache(t *testing.T) {
    21  	type get struct {
    22  		key  string
    23  		want any
    24  	}
    25  	type set struct {
    26  		key, value string
    27  	}
    28  
    29  	tests := []struct {
    30  		label string
    31  		steps []any
    32  	}{
    33  		{"empty cache", []any{
    34  			get{"a", nil},
    35  			get{"b", nil},
    36  		}},
    37  		{"zero-length string", []any{
    38  			set{"a", ""},
    39  			get{"a", ""},
    40  		}},
    41  		{"under capacity", []any{
    42  			set{"a", "123"},
    43  			set{"b", "456"},
    44  			get{"a", "123"},
    45  			get{"b", "456"},
    46  		}},
    47  		{"over capacity", []any{
    48  			set{"a", "123"},
    49  			set{"b", "456"},
    50  			set{"c", "78901"},
    51  			get{"a", nil},
    52  			get{"b", "456"},
    53  			get{"c", "78901"},
    54  		}},
    55  		{"access ordering", []any{
    56  			set{"a", "123"},
    57  			set{"b", "456"},
    58  			get{"a", "123"},
    59  			set{"c", "78901"},
    60  			get{"a", "123"},
    61  			get{"b", nil},
    62  			get{"c", "78901"},
    63  		}},
    64  	}
    65  
    66  	for _, test := range tests {
    67  		t.Run(test.label, func(t *testing.T) {
    68  			c := lru.New(10)
    69  			for i, step := range test.steps {
    70  				switch step := step.(type) {
    71  				case get:
    72  					if got := c.Get(step.key); got != step.want {
    73  						t.Errorf("#%d: c.Get(%q) = %q, want %q", i, step.key, got, step.want)
    74  					}
    75  				case set:
    76  					c.Set(step.key, step.value, len(step.value))
    77  				}
    78  			}
    79  		})
    80  	}
    81  }
    82  
    83  // TestConcurrency exercises concurrent access to the same entry.
    84  //
    85  // It is a copy of TestConcurrency from the filecache package.
    86  func TestConcurrency(t *testing.T) {
    87  	key := uniqueKey()
    88  	const N = 100 // concurrency level
    89  
    90  	// Construct N distinct values, each larger
    91  	// than a typical 4KB OS file buffer page.
    92  	var values [N][8192]byte
    93  	for i := range values {
    94  		if _, err := mathrand.Read(values[i][:]); err != nil {
    95  			t.Fatalf("rand: %v", err)
    96  		}
    97  	}
    98  
    99  	cache := lru.New(100 * 1e6) // 100MB cache
   100  
   101  	// get calls Get and verifies that the cache entry
   102  	// matches one of the values passed to Set.
   103  	get := func(mustBeFound bool) error {
   104  		got := cache.Get(key)
   105  		if got == nil {
   106  			if !mustBeFound {
   107  				return nil
   108  			}
   109  			return fmt.Errorf("Get did not return a value")
   110  		}
   111  		gotBytes := got.([]byte)
   112  		for _, want := range values {
   113  			if bytes.Equal(want[:], gotBytes) {
   114  				return nil // a match
   115  			}
   116  		}
   117  		return fmt.Errorf("Get returned a value that was never Set")
   118  	}
   119  
   120  	// Perform N concurrent calls to Set and Get.
   121  	// All sets must succeed.
   122  	// All gets must return nothing, or one of the Set values;
   123  	// there is no third possibility.
   124  	var group errgroup.Group
   125  	for i := range values {
   126  		i := i
   127  		v := values[i][:]
   128  		group.Go(func() error {
   129  			cache.Set(key, v, len(v))
   130  			return nil
   131  		})
   132  		group.Go(func() error { return get(false) })
   133  	}
   134  	if err := group.Wait(); err != nil {
   135  		if strings.Contains(err.Error(), "operation not supported") ||
   136  			strings.Contains(err.Error(), "not implemented") {
   137  			t.Skipf("skipping: %v", err)
   138  		}
   139  		t.Fatal(err)
   140  	}
   141  
   142  	// A final Get must report one of the values that was Set.
   143  	if err := get(true); err != nil {
   144  		t.Fatalf("final Get failed: %v", err)
   145  	}
   146  }
   147  
   148  // uniqueKey returns a key that has never been used before.
   149  func uniqueKey() (key [32]byte) {
   150  	if _, err := cryptorand.Read(key[:]); err != nil {
   151  		log.Fatalf("rand: %v", err)
   152  	}
   153  	return
   154  }