github.com/cilium/statedb@v0.3.2/part/map_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package part_test
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"iter"
    10  	"math/rand/v2"
    11  	"testing"
    12  
    13  	"github.com/cilium/statedb/part"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  	"gopkg.in/yaml.v3"
    17  )
    18  
    19  func TestStringMap(t *testing.T) {
    20  	var m part.Map[string, int]
    21  
    22  	//
    23  	// Operations on empty map
    24  	//
    25  
    26  	v, ok := m.Get("nonexisting")
    27  	assert.False(t, ok, "Get non-existing")
    28  	assert.Equal(t, 0, v)
    29  
    30  	assertIterEmpty := func(it iter.Seq2[string, int]) {
    31  		t.Helper()
    32  		for range it {
    33  			t.Fatalf("expected empty iterator")
    34  		}
    35  	}
    36  	assertIterEmpty(m.LowerBound(""))
    37  	assertIterEmpty(m.Prefix(""))
    38  	assertIterEmpty(m.All())
    39  
    40  	//
    41  	// Operations on non-empty map
    42  	//
    43  
    44  	// Ordered list of key-value pairs we're testing with.
    45  	// Prefix set so that Map keeps them in the same order.
    46  	kvs := []struct {
    47  		k string
    48  		v int
    49  	}{
    50  		{"1_one", 1},
    51  		{"2_two", 2},
    52  		{"3_three", 3},
    53  	}
    54  
    55  	// Set some values in two different ways.
    56  	m = m.Set("1_one", 1)
    57  	m = part.FromMap(m, map[string]int{
    58  		"2_two":   2,
    59  		"3_three": 3,
    60  	})
    61  
    62  	// Setting on a copy doeen't affect original
    63  	m.Set("4_four", 4)
    64  	_, ok = m.Get("4_four")
    65  	assert.False(t, ok, "Get non-existing")
    66  
    67  	// Getting a non-existing value still does the same.
    68  	v, ok = m.Get("nonexisting")
    69  	assert.False(t, ok, "Get non-existing")
    70  	assert.Equal(t, 0, v)
    71  
    72  	for _, kv := range kvs {
    73  		v, ok := m.Get(kv.k)
    74  		assert.True(t, ok, "Get %q", kv.k)
    75  		assert.Equal(t, v, kv.v)
    76  	}
    77  
    78  	expected := kvs
    79  	for k, v := range m.All() {
    80  		kv := expected[0]
    81  		expected = expected[1:]
    82  		assert.EqualValues(t, kv.k, k)
    83  		assert.EqualValues(t, kv.v, v)
    84  	}
    85  	assert.Empty(t, expected)
    86  
    87  	expected = kvs[1:]
    88  	for k, v := range m.LowerBound("2") {
    89  		kv := expected[0]
    90  		expected = expected[1:]
    91  		assert.EqualValues(t, kv.k, k)
    92  		assert.EqualValues(t, kv.v, v)
    93  	}
    94  	assert.Empty(t, expected)
    95  
    96  	expected = kvs[1:2]
    97  	for k, v := range m.Prefix("2") {
    98  		kv := expected[0]
    99  		expected = expected[1:]
   100  		assert.EqualValues(t, kv.k, k)
   101  		assert.EqualValues(t, kv.v, v)
   102  	}
   103  	assert.Empty(t, expected)
   104  
   105  	assert.Equal(t, 3, m.Len())
   106  
   107  	mOld := m
   108  	m = m.Delete(kvs[0].k)
   109  	_, ok = m.Get(kvs[0].k)
   110  	assert.False(t, ok, "Get after Delete")
   111  
   112  	_, ok = mOld.Get(kvs[0].k)
   113  	assert.True(t, ok, "Original modified by Delete")
   114  	mOld = mOld.Delete(kvs[0].k)
   115  	_, ok = mOld.Get(kvs[0].k)
   116  	assert.False(t, ok, "Get after Delete")
   117  
   118  	assert.Equal(t, 2, m.Len())
   119  }
   120  
   121  func TestUint64Map(t *testing.T) {
   122  	// TestStringMap tests most of the operations. We just check here that
   123  	// fromBytes and toBytes work and can iterate in the right order.
   124  	var m part.Map[uint64, int]
   125  	m = m.Set(42, 42)
   126  	m = m.Set(55, 55)
   127  	m = m.Set(72, 72)
   128  
   129  	v, ok := m.Get(42)
   130  	assert.True(t, ok, "Get 42")
   131  	assert.Equal(t, 42, v)
   132  
   133  	count := 0
   134  	expected := []uint64{55, 72}
   135  	for k, v := range m.LowerBound(55) {
   136  		kv := expected[0]
   137  		expected = expected[1:]
   138  		assert.EqualValues(t, kv, k)
   139  		assert.EqualValues(t, kv, v)
   140  		count++
   141  	}
   142  	assert.Equal(t, 2, count)
   143  }
   144  
   145  func TestRegisterKeyType(t *testing.T) {
   146  	type testKey struct {
   147  		X string
   148  	}
   149  	part.RegisterKeyType(func(k testKey) []byte { return []byte(k.X) })
   150  
   151  	var m part.Map[testKey, int]
   152  	m = m.Set(testKey{"hello"}, 123)
   153  
   154  	v, ok := m.Get(testKey{"hello"})
   155  	assert.True(t, ok, "Get 'hello'")
   156  	assert.Equal(t, 123, v)
   157  
   158  	for k, v := range m.All() {
   159  		assert.Equal(t, testKey{"hello"}, k)
   160  		assert.Equal(t, 123, v)
   161  	}
   162  }
   163  
   164  func TestMapJSON(t *testing.T) {
   165  	var m part.Map[string, int]
   166  	m = m.Set("foo", 1).Set("bar", 2).Set("baz", 3)
   167  
   168  	bs, err := json.Marshal(m)
   169  	require.NoError(t, err, "Marshal")
   170  
   171  	var m2 part.Map[string, int]
   172  	err = json.Unmarshal(bs, &m2)
   173  	require.NoError(t, err, "Unmarshal")
   174  	require.True(t, m.SlowEqual(m2), "SlowEqual")
   175  }
   176  
   177  func TestMapYAMLStringKey(t *testing.T) {
   178  	var m part.Map[string, int]
   179  	m = m.Set("foo", 1).Set("bar", 2).Set("baz", 3)
   180  
   181  	bs, err := yaml.Marshal(m)
   182  	require.NoError(t, err, "Marshal")
   183  
   184  	var m2 part.Map[string, int]
   185  	err = yaml.Unmarshal(bs, &m2)
   186  	require.NoError(t, err, "Unmarshal")
   187  	require.True(t, m.SlowEqual(m2), "SlowEqual")
   188  }
   189  
   190  func TestMapYAMLStructKey(t *testing.T) {
   191  	type key struct {
   192  		A int    `yaml:"a"`
   193  		B string `yaml:"b"`
   194  	}
   195  	part.RegisterKeyType[key](func(k key) []byte {
   196  		return []byte(fmt.Sprintf("%d-%s", k.A, k.B))
   197  	})
   198  	var m part.Map[key, int]
   199  	m = m.Set(key{1, "one"}, 1).Set(key{2, "two"}, 2).Set(key{3, "three"}, 3)
   200  
   201  	bs, err := yaml.Marshal(m)
   202  	require.NoError(t, err, "Marshal")
   203  
   204  	var m2 part.Map[key, int]
   205  	err = yaml.Unmarshal(bs, &m2)
   206  	require.NoError(t, err, "Unmarshal")
   207  	require.True(t, m.SlowEqual(m2), "SlowEqual")
   208  }
   209  
   210  func Benchmark_Uint64Map_Random(b *testing.B) {
   211  	numItems := 1000
   212  	keys := map[uint64]int{}
   213  	for len(keys) < numItems {
   214  		k := uint64(rand.Int64())
   215  		keys[k] = int(k)
   216  	}
   217  	for n := 0; n < b.N; n++ {
   218  		var m part.Map[uint64, int]
   219  		for k, v := range keys {
   220  			m = m.Set(k, v)
   221  			v2, ok := m.Get(k)
   222  			if !ok || v != v2 {
   223  				b.Fatalf("Get did not return value")
   224  			}
   225  		}
   226  	}
   227  	b.ReportMetric(float64(numItems*b.N)/b.Elapsed().Seconds(), "items/sec")
   228  }
   229  
   230  func Benchmark_Uint64Map_Sequential(b *testing.B) {
   231  	numItems := 1000
   232  
   233  	for n := 0; n < b.N; n++ {
   234  		var m part.Map[uint64, int]
   235  		for i := 0; i < numItems; i++ {
   236  			k := uint64(i)
   237  			m = m.Set(k, i)
   238  			v, ok := m.Get(k)
   239  			if !ok || v != i {
   240  				b.Fatalf("Get did not return value")
   241  			}
   242  		}
   243  	}
   244  	b.ReportMetric(float64(numItems*b.N)/b.Elapsed().Seconds(), "items/sec")
   245  }