Skip to content

Commit

Permalink
Made changes as per PR for lab7
Browse files Browse the repository at this point in the history
Removed error_test and added functions into store_test.go
Renamed sync_mapstore to syncstore.go
Various other renamings and changes

Added syncstore to lab 7
  • Loading branch information
Jim hejtmanek committed Jul 31, 2020
1 parent 7cf0b38 commit 9d75ceb
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 26 deletions.
11 changes: 6 additions & 5 deletions 07_errors/jimbotech/mapstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ func (s MapStore) length() int {
}

// CreatePuppy add a puppy to storage
// choose a unique member id that is currently not in the collection
// but will modify the member ID.
func (s MapStore) CreatePuppy(p *Puppy) (int32, error) {
if s == nil {
return 0, ErrNotConstructed
}
p.ID = rand.Int31()
for notUnique := true; notUnique; p.ID = rand.Int31() {
_, notUnique = s[p.ID]
}
sp := *p
s[p.ID] = &sp
return p.ID, nil
Expand All @@ -45,8 +48,7 @@ func (s MapStore) UpdatePuppy(id int32, puppy *Puppy) error {
if s == nil {
return ErrNotConstructed
}
_, err := s.ReadPuppy(id)
if err != nil {
if _, err := s.ReadPuppy(id); err != nil {
return err
}
puppy.ID = id
Expand All @@ -60,8 +62,7 @@ func (s MapStore) DeletePuppy(id int32) error {
if s == nil {
return ErrNotConstructed
}
_, err := s.ReadPuppy(id)
if err != nil {
if _, err := s.ReadPuppy(id); err != nil {
return err
}
delete(s, id)
Expand Down
17 changes: 9 additions & 8 deletions 07_errors/jimbotech/storer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"errors"
"strconv"
)

Expand Down Expand Up @@ -40,18 +39,20 @@ func (e *Error) Error() string {
}

const (
// ErrNegativeID error number if id is negative
ErrNegativeID = -1
// ErrNotFound error number if id not found
ErrNotFound = -2
// ecNegativeID error number if id is negative
ecNegativeID = iota
// ecNotFound error number if id not found
ecNotFound = iota
// ecNotConstructed if the interface was called without being constructed first
ecNotConstructed = iota
)

// ErrValueBelowZero error generated if the calu is below zero
var ErrValueBelowZero = &Error{Message: "id below 0", Code: ErrNegativeID}
var ErrValueBelowZero = &Error{Message: "id below 0", Code: ecNegativeID}

// ErrIDNotFound error if the requested ID is not in the store
var ErrIDNotFound = &Error{Message: "id not found", Code: ErrNotFound}
var ErrIDNotFound = &Error{Message: "id not found", Code: ecNotFound}

// ErrNotConstructed returned if the interface was called without
// first constructing the underlaying structure.
var ErrNotConstructed = errors.New("store not created")
var ErrNotConstructed = &Error{Message: "store not created", Code: ecNotConstructed}
72 changes: 59 additions & 13 deletions 07_errors/jimbotech/storer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ const grey = "grey"

func TestSuite(t *testing.T) {
suite.Run(t, &storesSuite{store: NewMapStore()})
suite.Run(t, &storesSuite{store: &SyncMapStore{}})
suite.Run(t, &storesSuite{store: &syncStore{}})
}

//SetupTest creates the correct empty map for each test
// SetupTest creates the correct empty map for each test
func (s *storesSuite) SetupTest() {
switch s.store.(type) {
case MapStore:
s.store = NewMapStore()
case *SyncMapStore:
s.store = &SyncMapStore{}
case *syncStore:
s.store = &syncStore{}
default:
s.Fail("Unknown Storer implementation")
}
Expand All @@ -49,10 +49,24 @@ func (s *storesSuite) TestReadSuccess() {
s.NotEqual(pup2, pup3)
}

func (s *storesSuite) TestReadIDNotFound() {
pup, err := s.store.ReadPuppy(1)
s.Require().Nil(pup)
s.Require().Equal(ErrIDNotFound, err)
}

func (s *storesSuite) TestReadValueBelowZero() {
pup, err := s.store.ReadPuppy(-1)
s.Require().Nil(pup)
s.Require().Equal(ErrValueBelowZero, err)
}

// TestCreateSuccess add to the store and verify
// by reading that it is in the store
func (s *storesSuite) TestCreateSuccess() {
pup := create(s)
pup, id, err := createWithErrorReturn(s)
s.Require().NoError(err)
s.Require().Equal(id, pup.ID, "Pup id must be set to actual id")
// Now modify the original and make sure the
// value in the store will not change
pup.Colour = black
Expand All @@ -62,18 +76,20 @@ func (s *storesSuite) TestCreateSuccess() {
s.Equal("kelpie", pup2.Breed)
s.Equal(brown, pup2.Colour)
s.Equal("indispensable", pup2.Value)
s.True(pup2.Colour == brown)
s.True(pup.Colour == black)
s.Equal(pup2.Colour, brown)
s.Equal(pup.Colour, black)
s.NotEqual(pup, pup2)
}

func create(s *storesSuite) *Puppy {
pup, _, _ := createWithErrorReturn(s)
return pup
}

func createWithErrorReturn(s *storesSuite) (*Puppy, int32, error) {
pup := Puppy{Breed: "kelpie", Colour: brown, Value: "indispensable"}
id, err := s.store.CreatePuppy(&pup)
s.Require().NoError(err)
s.Require().NotEqual(pup.ID, uint32(1))
s.Require().Equal(id, pup.ID, "Pup id must be set to actual id")
return &pup
return &pup, id, err
}

func (s *storesSuite) TestUpdateSuccess() {
Expand All @@ -85,12 +101,26 @@ func (s *storesSuite) TestUpdateSuccess() {
// now check by reading the updated value back and compare
pup3, err2 := s.store.ReadPuppy(pup.ID)
if s.Nil(err2, "Reading back updated value should work") {
s.True(pup2.Colour == brown)
s.True(pup3.Colour == black)
s.Equal(pup2.Colour, brown)
s.Equal(pup3.Colour, black)
s.NotEqual(pup2, *pup3)
}
}

func (s *storesSuite) TestUpdateIDNotFound() {
create(s)
pup := Puppy{Breed: "kelpie", Colour: "black", Value: "indispensable"}
err := s.store.UpdatePuppy(1, &pup)
s.Require().Equal(ErrIDNotFound, err)
}

func (s *storesSuite) TestUpdateValueBelowZero() {
create(s)
pup := Puppy{Breed: "kelpie", Colour: "black", Value: "indispensable"}
err := s.store.UpdatePuppy(-1, &pup)
s.Require().Equal(ErrValueBelowZero, err)
}

func (s *storesSuite) TestDeleteSuccess() {
pup := create(s)
err := s.store.DeletePuppy(pup.ID)
Expand All @@ -100,6 +130,16 @@ func (s *storesSuite) TestDeleteSuccess() {
s.NotEmpty(err.Error())
}

func (s *storesSuite) TestDeleteIDNotFound() {
err := s.store.DeletePuppy(1)
s.Require().Equal(ErrIDNotFound, err)
}

func (s *storesSuite) TestValueBelowZero() {
err := s.store.DeletePuppy(-1)
s.Require().Equal(ErrValueBelowZero, err)
}

func (s *storesSuite) TestMapChanges() {
s.Equal(0, s.mapper.length())
pup := Puppy{Breed: "kelpie", Colour: brown, Value: "high"}
Expand All @@ -114,3 +154,9 @@ func (s *storesSuite) TestMapChanges() {
s.Require().Nil(err, "Delete puppy failed")
s.Equal(0, s.mapper.length())
}

func (s *storesSuite) TestErrorNil() {
var err *Error
res := err.Error()
s.Assert().Equal("<nil>", res)
}
87 changes: 87 additions & 0 deletions 07_errors/jimbotech/syncstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"math/rand"
"sync"
)

// syncStore stores puppies threadsafe.
type syncStore struct {
m sync.Mutex
pm sync.Map
}

// length is not concorrency safe. As the go doc says:
// Range does not necessarily correspond to any consistent snapshot of the
// Map's contents: no key will be visited more than once, but if the value
// for any key is stored or deleted concurrently, Range may reflect any
// mapping for that key from any point during the Range call.
//
func (s *syncStore) length() int {
var length int
s.m.Lock()
defer s.m.Unlock()
s.pm.Range(func(key interface{}, value interface{}) bool {
length++
return true
})
return length
}

// CreatePuppy threadsafe adding a puppy to storage
// but will modify the member ID.
func (s *syncStore) CreatePuppy(p *Puppy) (int32, error) {
s.m.Lock()
defer s.m.Unlock()
for notUnique := true; notUnique; p.ID = rand.Int31() {
_, notUnique = s.pm.Load(p.ID)
}
sp := *p
s.pm.Store(p.ID, &sp)
return p.ID, nil
}

// ReadPuppy threadsafe retrieval of your puppy.
func (s *syncStore) ReadPuppy(id int32) (*Puppy, error) {
s.m.Lock()
defer s.m.Unlock()
return s.unsafeRetrievePuppy(id)
}

func (s *syncStore) unsafeRetrievePuppy(id int32) (*Puppy, error) {
if id < 0 {
return nil, ErrValueBelowZero
}
val, found := s.pm.Load(id)
if !found {
return nil, ErrIDNotFound
}
retPup := *val.(*Puppy)
return &retPup, nil
}

// UpdatePuppy threadsafe update your puppy store.
func (s *syncStore) UpdatePuppy(id int32, puppy *Puppy) error {
s.m.Lock()
defer s.m.Unlock()
_, err := s.unsafeRetrievePuppy(id)
if err != nil {
return err
}
puppy.ID = id
sp := *puppy
s.pm.Store(id, &sp)
return nil
}

// DeletePuppy threadsafe removal of the puppy from store.
func (s *syncStore) DeletePuppy(id int32) error {
s.m.Lock()
defer s.m.Unlock()
_, err := s.unsafeRetrievePuppy(id)
if err != nil {
return err
}
s.pm.Delete(id)
return nil
}

0 comments on commit 9d75ceb

Please sign in to comment.