Skip to content

Commit

Permalink
Add plugin.EntryMap
Browse files Browse the repository at this point in the history
We'll need this type to properly implement Delete.

Signed-off-by: Enis Inan <[email protected]>
  • Loading branch information
ekinanp committed Oct 22, 2019
1 parent 638e4ba commit ebc4b72
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
62 changes: 62 additions & 0 deletions plugin/entryMap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package plugin

import "sync"

// EntryMap is a thread-safe map of <entry_cname> => <entry_object>.
// It's API is (mostly) symmetric with sync.Map.
type EntryMap struct {
mp map[string]Entry
mux sync.RWMutex
}

func newEntryMap() *EntryMap {
return &EntryMap{
mp: make(map[string]Entry),
}
}

// Load retrieves an entry
func (m *EntryMap) Load(cname string) (Entry, bool) {
m.mux.RLock()
defer m.mux.RUnlock()

entry, ok := m.mp[cname]
return entry, ok
}

// Delete deletes the entry from the map
func (m *EntryMap) Delete(cname string) {
m.mux.Lock()
defer m.mux.Unlock()

delete(m.mp, cname)
}

// Len returns the number of entries in the map
func (m *EntryMap) Len() int {
m.mux.RLock()
defer m.mux.RUnlock()

return len(m.mp)
}

// Range iterates over the map, applying f to each (cname, entry)
// pair. If f returns false, then Each will break out of the loop.
func (m *EntryMap) Range(f func(string, Entry) bool) {
m.mux.RLock()
defer m.mux.RUnlock()

for cname, entry := range m.mp {
if !f(cname, entry) {
break
}
}
}

// Map returns m's underlying map. It can only be called by the tests.
func (m *EntryMap) Map() map[string]Entry {
if notRunningTests() {
panic("plugin.EntryMap#Map can only be called by the tests")
}
return m.mp
}
72 changes: 72 additions & 0 deletions plugin/entryMap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package plugin

import (
"testing"

"github.com/stretchr/testify/suite"
)

type EntryMapTestSuite struct {
suite.Suite
}

func (suite *EntryMapTestSuite) TestLoad_Delete_Len() {
m := newEntryMap()

_, ok := m.Load("foo")
suite.False(ok)

suite.Equal(0, m.Len())

expectedEntry := newCacheTestsMockEntry("foo")
m.mp["foo"] = expectedEntry

actualEntry, ok := m.Load("foo")
suite.Equal(expectedEntry, actualEntry)
suite.True(ok)

suite.Equal(1, m.Len())

m.Delete("foo")
_, ok = m.Load("foo")
suite.False(ok)

suite.Equal(0, m.Len())
}

func (suite *EntryMapTestSuite) TestRange() {
m := newEntryMap()

entries := []Entry{
newCacheTestsMockEntry("foo"),
newCacheTestsMockEntry("bar"),
}
for _, entry := range entries {
m.mp[CName(entry)] = entry
}

// Test that iteration works
entryMap := make(map[string]Entry)
m.Range(func(cname string, entry Entry) bool {
entryMap[cname] = entry
return true
})
suite.Equal(m.Map(), entryMap)

// Test that break works
count := 0
m.Range(func(cname string, entry Entry) bool {
if cname == "foo" {
return false
}
count++
return true
})
// <= 1 is to account for the fact that Go map's iteration order
// is random.
suite.True(count <= 1)
}

func TestEntryMap(t *testing.T) {
suite.Run(t, new(EntryMapTestSuite))
}

0 comments on commit ebc4b72

Please sign in to comment.