Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jfsmig committed May 24, 2022
1 parent 4ae814e commit a2a1d2f
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 29 deletions.
2 changes: 0 additions & 2 deletions sorted_cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ func (s SortedCmp[T]) Less(i, j int) bool { return s[i].Compare(s[j]) < 0 }
func (s *SortedCmp[T]) Add(a T) {
*s = append(*s, a)
switch nb := len(*s); nb {
case 0:
panic("yet another attack of a solar eruption")
case 1:
return
case 2:
Expand Down
132 changes: 130 additions & 2 deletions sorted_cmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,132 @@ func (i0 CmpInt) Compare(i1 CmpInt) int {
return int(i0) - int(i1)
}

func TestCmp_Unsorted(T *testing.T) {
func TestCmp_RemoveSorted(T *testing.T) {
bag := SortedCmp[CmpInt]{0, 1, 2, 3}
for _, v := range []CmpInt{-1, 4} {
if bag.Has(v) {
T.Fatal()
}
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
for _, v := range []CmpInt{0, 1, 2, 3} {
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
}

func TestCmp_RemoveUnsorted(T *testing.T) {
bag := SortedCmp[CmpInt]{0, 1, 2, 3}
for _, v := range []CmpInt{-1, 4} {
if bag.Has(v) {
T.Fatal()
}
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
for _, v := range []CmpInt{3, 2, 1, 0} {
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
}

func TestCmp_Lookup(T *testing.T) {
bag := SortedCmp[CmpInt]{0, 1, 2, 3}
for idx, v := range []CmpInt{0, 1, 2, 3} {
if !bag.Has(v) {
T.Fatal()
}
if x, ok := bag.Get(v); !ok {
T.Fatal()
} else if x != v {
T.Fatal()
}
if idx != bag.GetIndex(v) {
T.Fatal()
}
}
for _, v := range []CmpInt{-1, -2, 5, 6} {
if bag.Has(v) {
T.Fatal()
}
if x, ok := bag.Get(v); ok {
T.Fatal()
} else if x != 0 {
T.Fatal()
}
if -1 != bag.GetIndex(v) {
T.Fatal()
}
}
}

func TestCmp_Slice(T *testing.T) {
bag := SortedCmp[CmpInt]{0, 1, 2, 3}
testSlice := func(marker CmpInt, max uint32, expectations ...CmpInt) {
slice := bag.Slice(marker, max)
if len(slice) != len(expectations) {
T.Fatal()
}
for i, v := range slice {
if v != expectations[i] {
T.Fatal()
}
}
}
testSlice(bag[0], 2, bag[1], bag[2])
testSlice(bag[0]-1, 2, bag[0], bag[1])
testSlice(bag[len(bag)-1], 1)
testSlice(bag[0]-1, minSliceSize-1, bag[:minSliceSize]...)
testSlice(bag[0]-1, maxSliceSize+1, bag...)
}

func TestCmp_SliceLarge(T *testing.T) {
const total = 3 * maxSliceSize
var bag SortedCmp[CmpInt]
for i := 0; i < total; i++ {
bag.Add(CmpInt(i))
}
bag.Assert()
slice := bag.Slice(bag[0], total)
if len(slice) < minSliceSize || len(slice) > maxSliceSize {
T.Fatal()
}
}

func TestCmp_AppendUnsorted(T *testing.T) {
var bag SortedCmp[CmpInt]
bag.Append(3, 1, 0, 2)
T.Log("bag", bag)
bag.Assert()
if bag.Len() != 4 {
T.Fatalf("invalid length")
}
}

func TestCmp_AppendSorted(T *testing.T) {
var bag SortedCmp[CmpInt]
bag.Append(0, 1, 2, 3)
T.Log("bag", bag)
bag.Assert()
if bag.Len() != 4 {
T.Fatal()
}
}

func TestCmp_AddUnsorted(T *testing.T) {
var bag SortedCmp[CmpInt]
bag.Add(3)
bag.Add(1)
Expand All @@ -26,7 +151,7 @@ func TestCmp_Unsorted(T *testing.T) {
bag.Assert()
}

func TestCmp_Sorted(T *testing.T) {
func TestCmp_AddSorted(T *testing.T) {
var bag SortedCmp[CmpInt]
bag.Add(0)
bag.Add(1)
Expand Down Expand Up @@ -64,6 +189,9 @@ func (s SortedCmp[T]) Check() error {

// areItemsUnique validates the unicity of the elements in the array
func (s SortedCmp[T]) areItemsUnique() bool {
if s.Len() < 2 {
return true
}
lastId := s[0]
for _, a := range s[1:] {
if lastId.Compare(a) == 0 {
Expand Down
17 changes: 8 additions & 9 deletions sorted_obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ type WithPK[PkType Ordered] interface {
PK() PkType
}

// Len implements a method of the sort.Interface
func (s SortedObj[PkType, T]) Len() int { return len(s) }

// Swap implements a method of the sort.Interface
func (s SortedObj[PkType, T]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Less implements a method of the sort.Interface
func (s SortedObj[PkType, T]) Less(i, j int) bool { return s[i].PK() < s[j].PK() }

// Add introduces a new item in the sorted array, regardless the presence of another item with the same PRIMARY KEY
// and preserves the ordering of the array.
func (s *SortedObj[PkType, T]) Add(a T) {
*s = append(*s, a)
switch nb := len(*s); nb {
case 0:
panic("yet another attack of a solar eruption")
case 1:
return
case 2:
Expand All @@ -44,6 +47,8 @@ func (s *SortedObj[PkType, T]) Add(a T) {
}
}

// Append introduces several items in the sorted array, regardless the presence of other items with the same PRIMARY KEY
// and preserves the ordering of the array.
func (s *SortedObj[PkType, T]) Append(a ...T) {
*s = append(*s, a...)
sort.Sort(s)
Expand Down Expand Up @@ -88,15 +93,9 @@ func (s SortedObj[PkType, T]) Get(id PkType) (out T, ok bool) {

func (s SortedObj[PkType, T]) Has(id PkType) bool { return s.GetIndex(id) >= 0 }

// Remove forwards the call to RemovePK with the primary key of the given
// element. The PK() method must manage the case of a `nil` receiver.
func (s *SortedObj[PkType, T]) Remove(a T) {
s.RemovePK(a.PK())
}

// RemovePK identifies the position of the element with the given primary key
// and then removes it and restores the sorting of the set.
func (s *SortedObj[PkType, T]) RemovePK(pk PkType) {
func (s *SortedObj[PkType, T]) Remove(pk PkType) {
idx := s.GetIndex(pk)
if idx >= 0 && idx < len(*s) {
if len(*s) == 1 {
Expand Down
138 changes: 126 additions & 12 deletions sorted_obj_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,143 @@ type Obj struct {

func (o *Obj) PK() int64 { return o.pk }

func TestObj_Unsorted(T *testing.T) {
func TestObj_RemoveSorted(T *testing.T) {
bag := SortedObj[int64, *Obj]{&Obj{0}, &Obj{1}, &Obj{2}, &Obj{3}}
for _, v := range []int64{-1, 4} {
if bag.Has(v) {
T.Fatal()
}
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
for _, v := range []int64{0, 1, 2, 3} {
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
}

func TestObj_RemoveUnsorted(T *testing.T) {
bag := SortedObj[int64, *Obj]{&Obj{0}, &Obj{1}, &Obj{2}, &Obj{3}}
for _, v := range []int64{-1, 4} {
if bag.Has(v) {
T.Fatal()
}
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
for _, v := range []int64{3, 2, 1, 0} {
bag.Remove(v)
if bag.Has(v) {
T.Fatal()
}
bag.Assert()
}
}

func TestObj_Lookup(T *testing.T) {
bag := SortedObj[int64, *Obj]{&Obj{0}, &Obj{1}, &Obj{2}, &Obj{3}}
for idx, v := range []int64{0, 1, 2, 3} {
if !bag.Has(v) {
T.Fatal()
}
if x, ok := bag.Get(v); !ok {
T.Fatal()
} else if x.PK() != v {
T.Fatal()
}
if idx != bag.GetIndex(v) {
T.Fatal()
}
}
for _, v := range []int64{-1, -2, 5, 6} {
if bag.Has(v) {
T.Fatal()
}
if _, ok := bag.Get(v); ok {
T.Fatal()
}
if -1 != bag.GetIndex(v) {
T.Fatal()
}
}
}

func TestObj_Slice(T *testing.T) {
bag := SortedObj[int64, *Obj]{&Obj{0}, &Obj{1}, &Obj{2}, &Obj{3}}
testSlice := func(marker int64, max uint32, expectations ...*Obj) {
slice := bag.Slice(marker, max)
if len(slice) != len(expectations) {
T.Fatal()
}
for i, v := range slice {
if v.PK() != expectations[i].PK() {
T.Fatal()
}
}
}
testSlice(bag[0].PK(), 2, bag[1], bag[2])
testSlice(bag[0].PK()-1, 2, bag[0], bag[1])
testSlice(bag[len(bag)-1].PK(), 1)
testSlice(bag[0].PK()-1, minSliceSize-1, bag[:minSliceSize]...)
testSlice(bag[0].PK()-1, maxSliceSize+1, bag...)
}

func TestObj_SliceLarge(T *testing.T) {
const total = 3 * maxSliceSize
var bag SortedObj[int64, *Obj]
for i := int64(0); i < total; i++ {
bag.Add(&Obj{i})
}
bag.Assert()
slice := bag.Slice(bag[0].PK(), total)
if len(slice) < minSliceSize || len(slice) > maxSliceSize {
T.Fatal()
}
}

func TestObj_AppendUnsorted(T *testing.T) {
var bag SortedObj[int64, *Obj]
bag.Append(&Obj{3}, &Obj{1}, &Obj{0}, &Obj{2})
bag.Assert()
if bag.Len() != 4 {
T.Fatalf("invalid length")
}
}

func TestObj_AppendSorted(T *testing.T) {
var bag SortedObj[int64, *Obj]
bag.Append(&Obj{0}, &Obj{1}, &Obj{2}, &Obj{3})
bag.Assert()
if bag.Len() != 4 {
T.Fatal()
}
}

func TestObj_AddUnsorted(T *testing.T) {
var bag SortedObj[int64, *Obj]
bag.Add(&Obj{3})
bag.Add(&Obj{1})
bag.Add(&Obj{0})
bag.Add(&Obj{2})
T.Log("bag", bag)
bag.Assert()
}

func TestObj_Sorted(T *testing.T) {
func TestObj_AddSorted(T *testing.T) {
var bag SortedObj[int64, *Obj]
bag.Add(&Obj{0})
bag.Add(&Obj{1})
bag.Add(&Obj{2})
bag.Add(&Obj{3})
T.Log("bag", bag)
bag.Assert()

s := bag.Slice(1, 2)
T.Log("slice", s)
if len(s) != 2 {
panic("bad length")
} else if s[0].PK() != 2 && s[1].PK() != 3 {
panic("bad content")
}
}

// Assert panics if Check returns an error
Expand All @@ -64,6 +175,9 @@ func (s SortedObj[int64, T]) Check() error {

// areItemsUnique validates the unicity of the elements in the array
func (s SortedObj[int64, T]) areItemsUnique() bool {
if s.Len() < 2 {
return true
}
lastId := s[0].PK()
for _, a := range s[1:] {
if lastId == a.PK() {
Expand Down
2 changes: 0 additions & 2 deletions sorted_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ func (s SortedRaw[T]) Less(i, j int) bool { return s[i] < s[j] }
func (s *SortedRaw[T]) Add(a T) {
*s = append(*s, a)
switch nb := len(*s); nb {
case 0:
panic("yet another attack of a solar eruption")
case 1:
return
case 2:
Expand Down
Loading

0 comments on commit a2a1d2f

Please sign in to comment.