github.com/searKing/golang/go@v1.2.117/exp/maps/nested_test.go (about)

     1  // Copyright 2023 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 maps_test
     6  
     7  import (
     8  	"fmt"
     9  	"math/rand"
    10  	"reflect"
    11  	"runtime"
    12  	"sync/atomic"
    13  	"testing"
    14  
    15  	maps_ "github.com/searKing/golang/go/exp/maps"
    16  )
    17  
    18  type mapOp string
    19  
    20  const (
    21  	opLoad             = mapOp("Load")
    22  	opStore            = mapOp("Store")
    23  	opLoadOrStore      = mapOp("LoadOrStore")
    24  	opLoadAndDelete    = mapOp("LoadAndDelete")
    25  	opDelete           = mapOp("Delete")
    26  	opSwap             = mapOp("Swap")
    27  	opCompareAndSwap   = mapOp("CompareAndSwap")
    28  	opCompareAndDelete = mapOp("CompareAndDelete")
    29  )
    30  
    31  var mapOps = [...]mapOp{
    32  	opLoad,
    33  	opStore,
    34  	opLoadOrStore,
    35  	opLoadAndDelete,
    36  	opDelete,
    37  	opSwap,
    38  	opCompareAndSwap,
    39  	opCompareAndDelete,
    40  }
    41  
    42  // mapCall is a quick.Generator for calls on mapInterface.
    43  type mapCall[K ~string] struct {
    44  	op mapOp
    45  	k  []K
    46  	v  any
    47  }
    48  
    49  func (c mapCall[K]) apply(m mapInterface[K]) (any, bool) {
    50  	switch c.op {
    51  	case opLoad:
    52  		return m.Load(c.k)
    53  	case opStore:
    54  		m.Store(c.k, c.v)
    55  		return nil, false
    56  	case opLoadOrStore:
    57  		return m.LoadOrStore(c.k, c.v)
    58  	case opLoadAndDelete:
    59  		return m.LoadAndDelete(c.k)
    60  	case opDelete:
    61  		m.Delete(c.k)
    62  		return nil, false
    63  	case opSwap:
    64  		return m.Swap(c.k, c.v)
    65  	case opCompareAndSwap:
    66  		if m.CompareAndSwap(c.k, c.v, rand.Int()) {
    67  			m.Delete(c.k)
    68  			return c.v, true
    69  		}
    70  		return nil, false
    71  	case opCompareAndDelete:
    72  		if m.CompareAndDelete(c.k, c.v) {
    73  			if _, ok := m.Load(c.k); !ok {
    74  				return nil, true
    75  			}
    76  		}
    77  		return nil, false
    78  	default:
    79  		panic("invalid mapOp")
    80  	}
    81  }
    82  
    83  type mapResult struct {
    84  	value any
    85  	ok    bool
    86  }
    87  
    88  func (mr *mapResult) Reset() {
    89  	var z mapResult
    90  	*mr = z
    91  }
    92  
    93  func randKey[K ~string](r *rand.Rand) []K {
    94  	b := make([]K, r.Intn(4))
    95  	for i := range b {
    96  		b[i] = K('a' + byte(rand.Intn(26)))
    97  	}
    98  	return b
    99  }
   100  
   101  func randValue(r *rand.Rand) any {
   102  	b := make([]byte, r.Intn(4))
   103  	for i := range b {
   104  		b[i] = 'a' + byte(rand.Intn(26))
   105  	}
   106  	return string(b)
   107  }
   108  
   109  func (mapCall[K]) Generate(r *rand.Rand, size int) reflect.Value {
   110  	c := mapCall[K]{op: mapOps[rand.Intn(len(mapOps))], k: randKey[K](r)}
   111  	switch c.op {
   112  	case opStore, opLoadOrStore:
   113  		c.v = randValue(r)
   114  	}
   115  	return reflect.ValueOf(c)
   116  }
   117  
   118  func applyCalls[K ~string](m mapInterface[K], calls [2]mapCall[K]) (results []mapResult, final map[any]any) {
   119  	for _, c := range calls {
   120  		v, ok := c.apply(m)
   121  		results = append(results, mapResult{v, ok})
   122  	}
   123  
   124  	final = make(map[any]any)
   125  	m.Range(func(k []K, v any) bool {
   126  		final[fmt.Sprintf("%v", k)] = v
   127  		return true
   128  	})
   129  
   130  	return results, final
   131  }
   132  
   133  func applyMap[K ~string](calls [2]mapCall[K]) ([]mapResult, map[any]any) {
   134  	return applyCalls[K](make(maps_.NestedMap[K]), calls)
   135  }
   136  
   137  func TestIssue40999(t *testing.T) {
   138  	var m = maps_.NestedMap[*int]{}
   139  
   140  	// Since the miss-counting in missLocked (via Delete)
   141  	// compares the miss count with len(m.dirty),
   142  	// add an initial entry to bias len(m.dirty) above the miss count.
   143  	m.Store(nil, struct{}{})
   144  
   145  	var finalized uint32
   146  
   147  	// Set finalizers that count for collected keys. A non-zero count
   148  	// indicates that keys have not been leaked.
   149  	for atomic.LoadUint32(&finalized) == 0 {
   150  		p := new(int)
   151  		runtime.SetFinalizer(p, func(*int) {
   152  			atomic.AddUint32(&finalized, 1)
   153  		})
   154  		m.Store([]*int{p}, struct{}{})
   155  		m.Delete([]*int{p})
   156  		runtime.GC()
   157  	}
   158  }
   159  
   160  func TestNestedMapRangeCall(t *testing.T) { // Issue 46399
   161  	var m = maps_.NestedMap[int]{}
   162  	for i, v := range [3]string{"hello", "world", "Go"} {
   163  		m.Store([]int{i, i, i}, v)
   164  	}
   165  
   166  	var dummyKey = []int{42, 42, 42, 42}
   167  	m.Range(func(keys []int, value any) bool {
   168  		m.Range(func(keys []int, value any) bool {
   169  			// We should be able to load the key offered in the Range callback,
   170  			// because there are no concurrent Delete involved in this tested map.
   171  			if v, ok := m.Load(keys); !ok || !reflect.DeepEqual(v, value) {
   172  				t.Fatalf("Nested Range loads unexpected value, got %+v want %+v", v, value)
   173  			}
   174  
   175  			// We didn't keep dummyKey and a value into the map before, if somehow we loaded
   176  			// a value from such a key, meaning there must be an internal bug regarding
   177  			// nested range in the Map.
   178  			if _, loaded := m.LoadOrStore(dummyKey, "dummy"); loaded {
   179  				t.Fatalf("Nested Range loads unexpected value, want store a new value")
   180  			}
   181  
   182  			// Try to Store then LoadAndDelete the corresponding value with the key
   183  			// 42 to the Map. In this case, the key 42 and associated value should be
   184  			// removed from the Map. Therefore any future range won't observe key 42
   185  			// as we checked in above.
   186  			val := "maps_.NestedMap[int]"
   187  			m.Store(dummyKey, val)
   188  			if v, loaded := m.LoadAndDelete(dummyKey); !loaded || !reflect.DeepEqual(v, val) {
   189  				t.Fatalf("Nested Range loads unexpected value, got %v, want %v", v, val)
   190  			}
   191  			return true
   192  		})
   193  
   194  		// Remove key from Map on-the-fly.
   195  		m.Delete(keys)
   196  		return true
   197  	})
   198  
   199  	// After a Range of Delete, all keys should be removed and any
   200  	// further Range won't invoke the callback. Hence length remains 0.
   201  	length := 0
   202  	m.Range(func(keys []int, value any) bool {
   203  		length++
   204  		return true
   205  	})
   206  
   207  	if length != 0 {
   208  		t.Fatalf("Unexpected maps_.NestedMap[int] size, got %v want %v", length, 0)
   209  	}
   210  }
   211  
   212  func TestCompareAndSwap_NonExistingKey(t *testing.T) {
   213  	m := &maps_.NestedMap[int]{}
   214  	if m.CompareAndSwap([]int{404}, nil, 42) {
   215  		t.Fatalf("CompareAndSwap on an non-existing key succeeded")
   216  	}
   217  }
   218  
   219  var nestedMapTests = []struct {
   220  	calls []mapCall[string]
   221  	want  []mapResult
   222  }{
   223  	{
   224  		calls: []mapCall[string]{
   225  			{
   226  				op: opStore,
   227  				k:  nil,
   228  				v:  nil,
   229  			},
   230  			{
   231  				op: opLoad,
   232  				k:  nil,
   233  				v:  nil,
   234  			},
   235  			{
   236  				op: opStore,
   237  				k:  []string{"name"},
   238  				v:  "Alice",
   239  			},
   240  			{
   241  				op: opLoad,
   242  				k:  []string{"name"},
   243  			},
   244  			{
   245  				op: opLoad,
   246  				k:  []string{"sex"},
   247  			},
   248  			{
   249  				op: opLoadOrStore,
   250  				k:  []string{"sex"},
   251  				v:  "Male",
   252  			},
   253  			{
   254  				op: opLoadAndDelete,
   255  				k:  []string{"sex_to_delete"},
   256  			},
   257  			{
   258  				op: opStore,
   259  				k:  []string{"sex_to_delete"},
   260  				v:  "Female",
   261  			},
   262  			{
   263  				op: opLoadAndDelete,
   264  				k:  []string{"sex_to_delete"},
   265  			},
   266  			{
   267  				op: opLoadOrStore,
   268  				k:  []string{"sex_to_delete"},
   269  				v:  "Middle",
   270  			},
   271  			{
   272  				op: opCompareAndSwap,
   273  				k:  []string{"country", "province"},
   274  				v:  "Nanjing",
   275  			},
   276  			{
   277  				op: opLoadOrStore,
   278  				k:  []string{"country", "province"},
   279  				v:  "Shanghai",
   280  			},
   281  			{
   282  				op: opLoad,
   283  				k:  []string{"country", "province"},
   284  			},
   285  			{
   286  				op: opCompareAndDelete,
   287  				k:  []string{"country", "province"},
   288  				v:  "Nanjing",
   289  			},
   290  			{
   291  				op: opLoad,
   292  				k:  []string{"country", "province"},
   293  			},
   294  			{
   295  				op: opCompareAndDelete,
   296  				k:  []string{"country", "province"},
   297  				v:  "Shanghai",
   298  			},
   299  			{
   300  				op: opLoad,
   301  				k:  []string{"country", "province"},
   302  			},
   303  			{
   304  				op: opDelete,
   305  				k:  []string{"country", "province"},
   306  			},
   307  			{
   308  				op: opLoad,
   309  				k:  []string{"country", "province"},
   310  			},
   311  		},
   312  		want: []mapResult{
   313  			{
   314  				value: nil,
   315  				ok:    false,
   316  			},
   317  			{
   318  				value: nil,
   319  				ok:    false,
   320  			},
   321  			{
   322  				value: nil,
   323  				ok:    false,
   324  			},
   325  			{
   326  				value: "Alice",
   327  				ok:    true,
   328  			},
   329  			{
   330  				value: nil,
   331  				ok:    false,
   332  			},
   333  			{
   334  				value: "Male",
   335  				ok:    false,
   336  			},
   337  			{
   338  				value: nil,
   339  				ok:    false,
   340  			},
   341  			{
   342  				value: nil,
   343  				ok:    false,
   344  			},
   345  			{
   346  				value: "Female",
   347  				ok:    true,
   348  			},
   349  			{
   350  				value: "Middle",
   351  				ok:    false,
   352  			},
   353  			{
   354  				value: nil,
   355  				ok:    false,
   356  			},
   357  			{
   358  				value: "Shanghai",
   359  				ok:    false,
   360  			},
   361  			{
   362  				value: "Shanghai",
   363  				ok:    true,
   364  			},
   365  			{
   366  				ok: false,
   367  			},
   368  			{
   369  				value: "Shanghai",
   370  				ok:    true,
   371  			},
   372  			{
   373  				ok: true,
   374  			},
   375  			{
   376  				value: nil,
   377  				ok:    false,
   378  			},
   379  			{
   380  				value: nil,
   381  				ok:    false,
   382  			},
   383  			{
   384  				value: nil,
   385  				ok:    false,
   386  			},
   387  		},
   388  	},
   389  	{
   390  		calls: []mapCall[string]{
   391  			{
   392  				op: opStore,
   393  				k:  []string{"name"},
   394  				v:  "Alice",
   395  			},
   396  			{
   397  				op: opLoad,
   398  				k:  []string{"name"},
   399  			},
   400  			{
   401  				op: opStore,
   402  				k:  []string{"name", "sex"},
   403  				v:  "Female",
   404  			},
   405  			{
   406  				op: opLoad,
   407  				k:  []string{"name"},
   408  			},
   409  			{
   410  				op: opLoad,
   411  				k:  []string{"name", "sex"},
   412  			},
   413  			{
   414  				op: opDelete,
   415  				k:  []string{"name"},
   416  			},
   417  			{
   418  				op: opLoad,
   419  				k:  []string{"name"},
   420  			},
   421  			{
   422  				op: opLoad,
   423  				k:  []string{"name", "sex"},
   424  			},
   425  		},
   426  		want: []mapResult{
   427  			{},
   428  			{
   429  				value: "Alice",
   430  				ok:    true,
   431  			},
   432  			{
   433  				value: nil,
   434  				ok:    false,
   435  			},
   436  			{
   437  				value: maps_.NestedMap[string]{"sex": "Female"},
   438  				ok:    true,
   439  			},
   440  			{
   441  				value: "Female",
   442  				ok:    true,
   443  			},
   444  			{},
   445  			{
   446  				value: nil,
   447  				ok:    false,
   448  			},
   449  			{
   450  				value: nil,
   451  				ok:    false,
   452  			},
   453  		},
   454  	},
   455  }
   456  
   457  func TestNestedMap(t *testing.T) {
   458  	for i, test := range nestedMapTests {
   459  		m := &maps_.NestedMap[string]{}
   460  		var mr mapResult
   461  		for j, c := range test.calls {
   462  			mr.Reset()
   463  			mr.value, mr.ok = c.apply(m)
   464  			if !reflect.DeepEqual(mr, test.want[j]) {
   465  				t.Errorf("#%d-%d: %s(%v, %v) = %v, want %v", i, j, c.op, c.k, c.v, mr, test.want[j])
   466  				break
   467  			}
   468  		}
   469  	}
   470  }