Files
libopenapi/orderedmap/orderedmap_test.go
2024-08-26 20:42:40 +01:00

489 lines
12 KiB
Go

package orderedmap_test
import (
"context"
"errors"
"fmt"
"io"
"sync/atomic"
"testing"
"time"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestOrderedMap(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
m := orderedmap.New[string, int]()
assert.Equal(t, m.Len(), 0)
assert.Nil(t, m.First())
})
t.Run("First()", func(t *testing.T) {
const mapSize = 1000
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("foobar_%d", i), i)
}
assert.Equal(t, m.Len(), mapSize)
for i := 0; i < mapSize; i++ {
assert.Equal(t, i, m.GetOrZero(fmt.Sprintf("foobar_%d", i)))
}
var i int
for pair := m.First(); pair != nil; pair = pair.Next() {
assert.Equal(t, fmt.Sprintf("foobar_%d", i), pair.Key())
assert.Equal(t, fmt.Sprintf("foobar_%d", i), *pair.KeyPtr())
assert.Equal(t, i, pair.Value())
assert.Equal(t, i, *pair.ValuePtr())
i++
require.LessOrEqual(t, i, mapSize)
}
assert.Equal(t, mapSize, i)
})
t.Run("Get()", func(t *testing.T) {
const mapSize = 1000
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), 1000+i)
}
for i := 0; i < mapSize; i++ {
actual, ok := m.Get(fmt.Sprintf("key%d", i))
assert.True(t, ok)
assert.Equal(t, 1000+i, actual)
}
_, ok := m.Get("bogus")
assert.False(t, ok)
})
t.Run("GetOrZero()", func(t *testing.T) {
const mapSize = 1000
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), 1000+i)
}
for i := 0; i < mapSize; i++ {
actual := m.GetOrZero(fmt.Sprintf("key%d", i))
assert.Equal(t, 1000+i, actual)
}
assert.Equal(t, 0, m.GetOrZero("bogus"))
})
}
func TestMap(t *testing.T) {
t.Run("Len()", func(t *testing.T) {
const mapSize = 100
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
assert.Equal(t, mapSize, m.Len())
assert.Equal(t, mapSize, orderedmap.Len(m))
t.Run("Nil pointer", func(t *testing.T) {
var m *orderedmap.Map[string, int]
assert.Zero(t, orderedmap.Len(m))
})
})
t.Run("Iterate()", func(t *testing.T) {
const mapSize = 10
t.Run("Empty", func(t *testing.T) {
m := orderedmap.New[string, int]()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := orderedmap.Iterate(ctx, m)
for range c {
t.Fatal("Expected no data")
}
requireClosed(t, c)
})
t.Run("Full iteration", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var i int
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := orderedmap.Iterate(ctx, m)
for pair := range c {
assert.Equal(t, fmt.Sprintf("key%d", i), pair.Key())
assert.Equal(t, fmt.Sprintf("key%d", i), *pair.KeyPtr())
assert.Equal(t, i+1000, pair.Value())
assert.Equal(t, i+1000, *pair.ValuePtr())
i++
require.LessOrEqual(t, i, mapSize)
}
assert.Equal(t, mapSize, i)
requireClosed(t, c)
})
t.Run("Partial iteration", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var i int
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := orderedmap.Iterate(ctx, m)
for pair := range c {
assert.Equal(t, fmt.Sprintf("key%d", i), pair.Key())
assert.Equal(t, fmt.Sprintf("key%d", i), *pair.KeyPtr())
assert.Equal(t, i+1000, pair.Value())
assert.Equal(t, i+1000, *pair.ValuePtr())
i++
if i >= mapSize/2 {
break
}
}
cancel()
time.Sleep(10 * time.Millisecond)
requireClosed(t, c)
assert.Equal(t, mapSize/2, i)
})
})
t.Run("TranslateMapParallel()", func(t *testing.T) {
const mapSize = 1000
t.Run("Happy path", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var translateCounter int64
translateFunc := func(pair orderedmap.Pair[string, int]) (string, error) {
result := fmt.Sprintf("foobar %d", pair.Value())
atomic.AddInt64(&translateCounter, 1)
return result, nil
}
var resultCounter int
resultFunc := func(value string) error {
assert.Equal(t, fmt.Sprintf("foobar %d", resultCounter+1000), value)
resultCounter++
return nil
}
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
require.NoError(t, err)
assert.Equal(t, int64(mapSize), translateCounter)
assert.Equal(t, mapSize, resultCounter)
})
t.Run("Error in translate", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
translateFunc := func(pair orderedmap.Pair[string, int]) (string, error) {
return "", errors.New("Foobar")
}
var resultCounter int
resultFunc := func(value string) error {
resultCounter++
return nil
}
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
require.ErrorContains(t, err, "Foobar")
assert.Zero(t, resultCounter)
})
t.Run("Error in result", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
translateFunc := func(pair orderedmap.Pair[string, int]) (string, error) {
return "", nil
}
var resultCounter int
resultFunc := func(value string) error {
resultCounter++
return errors.New("Foobar")
}
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
require.ErrorContains(t, err, "Foobar")
assert.Equal(t, 1, resultCounter)
})
t.Run("EOF in translate", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
translateFunc := func(pair orderedmap.Pair[string, int]) (string, error) {
return "", io.EOF
}
var resultCounter int
resultFunc := func(value string) error {
resultCounter++
return nil
}
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
require.NoError(t, err)
assert.Zero(t, resultCounter)
})
t.Run("EOF in result", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
translateFunc := func(pair orderedmap.Pair[string, int]) (string, error) {
return "", nil
}
var resultCounter int
resultFunc := func(value string) error {
resultCounter++
return io.EOF
}
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
require.NoError(t, err)
assert.Equal(t, 1, resultCounter)
})
})
}
func TestFirst(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
pair := orderedmap.First[string, int](nil)
require.Nil(t, pair)
})
t.Run("Single item", func(t *testing.T) {
m := orderedmap.New[string, int]()
m.Set("key", 1)
var count int
for pair := orderedmap.First(m); pair != nil; pair = pair.Next() {
count++
}
assert.Equal(t, 1, count)
})
t.Run("Many items", func(t *testing.T) {
const mapSize = 100
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var count int
for pair := orderedmap.First(m); pair != nil; pair = pair.Next() {
count++
}
assert.Equal(t, mapSize, count)
})
}
func TestLen(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
m := (*orderedmap.Map[string, int])(nil)
require.Zero(t, orderedmap.Len(m))
})
t.Run("Single item", func(t *testing.T) {
m := orderedmap.New[string, int]()
m.Set("key", 1)
assert.Equal(t, 1, orderedmap.Len(m))
})
t.Run("Many items", func(t *testing.T) {
const mapSize = 100
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
assert.Equal(t, mapSize, orderedmap.Len(m))
})
}
func TestFromPairs(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
m := orderedmap.FromPairs[string, int]()
require.NotNil(t, m)
assert.Zero(t, m.Len())
})
t.Run("Single item", func(t *testing.T) {
m := orderedmap.FromPairs(
orderedmap.NewPair[string, int]("key", 1),
)
require.NotNil(t, m)
assert.Equal(t, 1, m.Len())
pair := m.First()
assert.Equal(t, "key", pair.Key())
assert.Equal(t, 1, pair.Value())
assert.Nil(t, pair.Next())
})
t.Run("Many items", func(t *testing.T) {
const mapSize = 100
var pairs []orderedmap.Pair[string, int]
for i := 0; i < mapSize; i++ {
key := fmt.Sprintf("key%d", i)
pairs = append(pairs, orderedmap.NewPair[string, int](key, i+1000))
}
m := orderedmap.FromPairs(pairs...)
require.NotNil(t, m)
assert.Equal(t, mapSize, m.Len())
var count int
for pair := m.First(); pair != nil; pair = pair.Next() {
expectedKey := fmt.Sprintf("key%d", count)
assert.Equal(t, expectedKey, pair.Key())
assert.Equal(t, count+1000, pair.Value())
count++
require.LessOrEqual(t, count, mapSize)
}
assert.Equal(t, mapSize, count)
})
}
func TestIterators(t *testing.T) {
om := orderedmap.New[int, any]()
om.Set(1, "bar")
om.Set(2, 28)
om.Set(3, 100)
om.Set(4, "baz")
om.Set(5, "28")
om.Set(6, "100")
om.Set(7, "baz")
om.Set(8, "baz")
expectedKeys := []int{1, 2, 3, 4, 5, 6, 7, 8}
expectedKeysFromNewest := []int{8, 7, 6, 5, 4, 3, 2, 1}
expectedValues := []any{"bar", 28, 100, "baz", "28", "100", "baz", "baz"}
expectedValuesFromNewest := []any{"baz", "baz", "100", "28", "baz", 100, 28, "bar"}
var keys []int
var values []any
for k, v := range om.FromOldest() {
keys = append(keys, k)
values = append(values, v)
}
assert.Equal(t, expectedKeys, keys)
assert.Equal(t, expectedValues, values)
keys, values = []int{}, []any{}
for k, v := range om.FromNewest() {
keys = append(keys, k)
values = append(values, v)
}
assert.Equal(t, expectedKeysFromNewest, keys)
assert.Equal(t, expectedValuesFromNewest, values)
keys = []int{}
for k := range om.KeysFromOldest() {
keys = append(keys, k)
}
assert.Equal(t, expectedKeys, keys)
keys = []int{}
for k := range om.KeysFromNewest() {
keys = append(keys, k)
}
assert.Equal(t, expectedKeysFromNewest, keys)
values = []any{}
for v := range om.ValuesFromOldest() {
values = append(values, v)
}
assert.Equal(t, expectedValues, values)
values = []any{}
for v := range om.ValuesFromNewest() {
values = append(values, v)
}
assert.Equal(t, expectedValuesFromNewest, values)
}
func TestIteratorsFrom(t *testing.T) {
om := orderedmap.New[int, any]()
om.Set(1, "bar")
om.Set(2, 28)
om.Set(3, 100)
om.Set(4, "baz")
om.Set(5, "28")
om.Set(6, "100")
om.Set(7, "baz")
om.Set(8, "baz")
om2 := orderedmap.From(om.FromOldest())
expectedKeys := []int{1, 2, 3, 4, 5, 6, 7, 8}
expectedValues := []any{"bar", 28, 100, "baz", "28", "100", "baz", "baz"}
var keys []int
var values []any
for k, v := range om2.FromOldest() {
keys = append(keys, k)
values = append(values, v)
}
assert.Equal(t, expectedKeys, keys)
assert.Equal(t, expectedValues, values)
expectedKeysFromNewest := []int{8, 7, 6, 5, 4, 3, 2, 1}
expectedValuesFromNewest := []any{"baz", "baz", "100", "28", "baz", 100, 28, "bar"}
om2 = orderedmap.From(om.FromNewest())
keys = []int{}
values = []any{}
for k, v := range om2.FromOldest() {
keys = append(keys, k)
values = append(values, v)
}
assert.Equal(t, expectedKeysFromNewest, keys)
assert.Equal(t, expectedValuesFromNewest, values)
}
func requireClosed[K comparable, V any](t *testing.T, c <-chan orderedmap.Pair[K, V]) {
select {
case pair := <-c:
require.Nil(t, pair, "Expected channel to be closed")
case <-time.After(100 * time.Millisecond):
t.Fatal("Timeout reading channel; expected channel to be closed")
}
}