Feat: LockableList (pushpop)

This commit is contained in:
kayos@tcp.direct 2023-03-23 00:41:11 -07:00
parent c7b1d5eba5
commit c167129c6d
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
5 changed files with 876 additions and 1 deletions

@ -93,7 +93,7 @@ const charsetWithUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12
func RandStr(size int) string {
buf := make([]byte, size)
for i := 0; i != size; i++ {
buf[i] = charset[uint32(RNG(36))%uint32(len(charset))]
buf[i] = charset[GetOptimizedRand().Uint32()%uint32(len(charset))]
}
return string(buf)
}

1
go.mod

@ -3,6 +3,7 @@ module git.tcp.direct/kayos/common
go 1.19
require (
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
golang.org/x/crypto v0.7.0
nullprogram.com/x/rng v1.1.0

2
go.sum

@ -1,3 +1,5 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=

310
list/list.go Normal file

@ -0,0 +1,310 @@
// Package list implements a locking list.l
package list
import (
"container/list"
"errors"
"sync"
)
var (
ErrElementNotInList = errors.New("element not in list")
ErrMarkNotInList = errors.New("mark not in list")
ErrNilValue = errors.New("nil element")
ErrUninitialized = errors.New("uninitialized list")
)
type LockingList struct {
l *list.List
*sync.RWMutex
}
func (ll *LockingList) Lock() error {
if ll == nil || ll.RWMutex == nil {
return ErrUninitialized
}
ll.RWMutex.Lock()
return nil
}
func (ll *LockingList) Unlock() {
if ll.RWMutex != nil {
ll.RWMutex.Unlock()
}
}
func (ll *LockingList) RLock() error {
switch {
case
ll == nil,
ll.RWMutex == nil,
ll.l == nil:
return ErrUninitialized
default:
//
}
ll.RWMutex.RLock()
return nil
}
func (ll *LockingList) RUnlock() {
if ll.RWMutex != nil {
ll.RWMutex.RUnlock()
}
}
func New() *LockingList {
ll := &LockingList{
l: list.New(),
RWMutex: &sync.RWMutex{},
}
return ll
}
func (ll *LockingList) wrapElement(e *list.Element) *Element {
if e == nil {
return nil
}
return &Element{
list: ll,
Element: e,
}
}
// Init initializes or clears list l.
func (ll *LockingList) Init() *LockingList {
if ll.l != nil {
_ = ll.Lock()
ll.l.Init()
ll.Unlock()
return ll
}
ll.l = list.New()
ll.RWMutex = &sync.RWMutex{}
return ll
}
func (ll *LockingList) InsertAfter(v any, mark *Element) (*Element, error) {
if err := ll.check(v, mark, true); err != nil {
return nil, err
}
_ = ll.Lock()
res := ll.l.InsertAfter(v, mark.Element)
ll.Unlock()
return ll.wrapElement(res), nil
}
func (ll *LockingList) InsertBefore(v any, mark *Element) (*Element, error) {
if err := ll.check(v, mark, true); err != nil {
return nil, err
}
_ = ll.Lock()
res := ll.wrapElement(
ll.l.InsertBefore(v, mark.Element),
)
ll.Unlock()
return res, nil
}
func (ll *LockingList) Len() int {
_ = ll.RLock()
l := ll.l.Len()
ll.RUnlock()
return l
}
func (ll *LockingList) MoveAfter(e, mark *Element) error {
_ = ll.Lock()
ll.l.MoveAfter(e.Element, mark.Element)
ll.Unlock()
return nil
}
func (ll *LockingList) Front() *Element {
_ = ll.RLock()
e := ll.l.Front()
ll.RUnlock()
return ll.wrapElement(e)
}
func (ll *LockingList) Back() *Element {
_ = ll.RLock()
e := ll.l.Back()
ll.RUnlock()
return ll.wrapElement(e)
}
func (ll *LockingList) MoveToBack(e *Element) error {
_ = ll.Lock()
ll.l.MoveToBack(e.Element)
ll.Unlock()
return nil
}
func (ll *LockingList) MoveToFront(e *Element) error {
_ = ll.Lock()
ll.l.MoveToFront(e.Element)
ll.Unlock()
return nil
}
func (ll *LockingList) PushBack(v any) *Element {
if ll.l == nil {
ll.Init()
}
_ = ll.Lock()
e := ll.l.PushBack(v)
ll.Unlock()
return ll.wrapElement(e)
}
func (ll *LockingList) PushFront(v any) *Element {
if ll.l == nil {
ll.Init()
}
if err := ll.Lock(); err != nil {
return nil
}
e := ll.l.PushFront(v)
ll.Unlock()
return ll.wrapElement(e)
}
func (ll *LockingList) Remove(elm *Element) error {
if ll.l == nil {
return ErrUninitialized
}
if err := ll.check(elm, nil, false); err != nil {
return err
}
_ = ll.Lock()
_ = ll.l.Remove(elm.Element)
elm.list = nil // avoid memory leaks
elm.Element = nil // avoid memory leaks
ll.Unlock()
return nil
}
// Rotate moves the first element to the back of the list and returns it.
func (ll *LockingList) Rotate() *Element {
if ll.l == nil {
ll.Init()
}
if ll.Len() < 1 {
return nil
}
_ = ll.Lock()
e := ll.l.Front()
ll.l.MoveToBack(e)
ll.Unlock()
return ll.wrapElement(e)
}
func (ll *LockingList) Push(item any) (err error) {
if ll.l == nil {
ll.Init()
}
if err = ll.Lock(); err != nil {
return err
}
ll.l.PushBack(item)
ll.Unlock()
return nil
}
func (ll *LockingList) Pop() any {
if ll.Len() < 1 {
return nil
}
_ = ll.Lock()
e := ll.l.Back()
ll.l.Remove(e)
ll.Unlock()
return e.Value
}
func (ll *LockingList) PushBackList(other *LockingList) error {
if ll.l == nil {
ll.Init()
}
_ = ll.Lock()
ll.l.PushBackList(other.l)
ll.Unlock()
return nil
}
func (ll *LockingList) PushFrontList(other *LockingList) error {
if ll.l == nil {
ll.Init()
}
_ = ll.Lock()
ll.l.PushFrontList(other.l)
ll.Unlock()
return nil
}
type Element struct {
*list.Element
list *LockingList
}
func (e *Element) Value() any {
if e == nil {
return nil
}
if e.Element == nil {
return nil
}
return e.Element.Value
}
func (e *Element) Next() *Element {
if e == nil {
return nil
}
if err := e.list.RLock(); err != nil {
return nil
}
ne := e.list.wrapElement(e.Element.Next())
e.list.RUnlock()
return ne
}
func (e *Element) Prev() *Element {
if e == nil {
return nil
}
if err := e.list.RLock(); err != nil {
return nil
}
pe := e.list.wrapElement(e.Element.Prev())
e.list.RUnlock()
return pe
}
func (ll *LockingList) check(item any, mark *Element, needsMark bool) (err error) {
var elm *Element
var isElement bool
elm, isElement = item.(*Element)
if err = ll.RLock(); err != nil {
return err
}
switch {
case
needsMark && mark == nil,
needsMark && mark.list != ll:
err = ErrMarkNotInList
case
isElement && elm.Element == nil,
isElement && elm.list != ll:
err = ErrElementNotInList
default:
err = nil
}
ll.RUnlock()
return
}

562
list/list_test.go Normal file

@ -0,0 +1,562 @@
package list
import (
"container/list"
"errors"
"reflect"
"sync"
"testing"
"unsafe"
)
// Adapted from golang source
func checkListLen(t *testing.T, l *LockingList, length int) bool {
t.Helper()
if n := l.Len(); n != length {
t.Errorf("l.Len() = %d, want %d", n, length)
return false
}
return true
}
// Adapted from golang source
func checkList(t *testing.T, l *LockingList, es []any) {
t.Helper()
if !checkListLen(t, l, len(es)) {
return
}
i := 0
for e := l.Front(); e != nil; e = e.Next() {
le := e.Value()
if le != es[i] {
t.Errorf("\uF630 elt[%d].Value = %v, want %v", i, le, es[i])
}
// t.Logf("\uF634 elt[%d].Value = %v, want %v", i, le, es[i])
i++
}
}
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func checkListPointers(t *testing.T, ll *LockingList, es []*list.Element) {
if !checkListLen(t, ll, len(es)) {
return
}
newList := reflect.New(reflect.TypeOf(*ll.l)).Elem()
root := newList.FieldByName("root")
// zero length lists must be the zero value or properly initialized (sentinel circle)
if len(es) == 0 {
next := root.FieldByName("next")
prev := root.FieldByName("prev")
if !next.IsNil() && next != root || !prev.IsNil() && prev != root {
t.Errorf("l.root.next = %v; should be nil or %v",
next, root.Type(),
)
}
if !prev.IsNil() && next != root || !prev.IsNil() && prev != root {
t.Errorf("l.root.prev = %p; should be nil or %v",
prev.Type(), root.Type(),
)
}
return
}
}
// Adapted from golang source
func TestExtending(t *testing.T) { //nolint:funlen,gocyclo
t.Parallel()
l1 := New()
l2 := New()
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l2.PushBack(4)
l2.PushBack(5)
l3 := New()
if err := l3.PushBackList(l1); err != nil {
t.Error(errors.New("PushBackList failed"))
}
checkList(t, l3, []any{1, 2, 3})
if err := l3.PushBackList(l2); err != nil {
t.Error(errors.New("PushBackList failed"))
}
checkList(t, l3, []any{1, 2, 3, 4, 5})
l3 = New()
if err := l3.PushFrontList(l2); err != nil {
t.Error(errors.New("PushFrontList failed"))
}
checkList(t, l3, []any{4, 5})
if err := l3.PushFrontList(l1); err != nil {
t.Error(errors.New("PushFrontList failed"))
}
checkList(t, l3, []any{1, 2, 3, 4, 5})
checkList(t, l1, []any{1, 2, 3})
checkList(t, l2, []any{4, 5})
l3 = New()
if err := l3.PushBackList(l1); err != nil {
t.Error(errors.New("PushBackList failed"))
}
checkList(t, l3, []any{1, 2, 3})
if err := l3.PushBackList(l3); err != nil {
t.Error(errors.New("PushBackList failed"))
}
checkList(t, l3, []any{1, 2, 3, 1, 2, 3})
l3 = New()
if err := l3.PushFrontList(l1); err != nil {
return
}
checkList(t, l3, []any{1, 2, 3})
if err := l3.PushFrontList(l3); err != nil {
t.Error(errors.New("PushFrontList failed"))
}
checkList(t, l3, []any{1, 2, 3, 1, 2, 3})
l3 = New()
if err := l1.PushBackList(l3); err != nil {
t.Error(errors.New("PushBackList failed"))
}
checkList(t, l1, []any{1, 2, 3})
if err := l1.PushFrontList(l3); err != nil {
t.Error(errors.New("PushFrontList failed"))
}
checkList(t, l1, []any{1, 2, 3})
}
// Adapted from golang source
func TestIssue4103(t *testing.T) {
t.Parallel()
l1 := New()
l1.PushBack(1)
l1.PushBack(2)
l2 := New()
l2.PushBack(3)
l2.PushBack(4)
e := l1.Front()
err := l2.Remove(e)
if err == nil {
t.Errorf("l2.Remove(e) = %v, want ErrElementNotInList", err)
}
if !errors.Is(err, ErrElementNotInList) {
t.Errorf("l2.Remove(e) = %v, want ErrElementNotInList", err)
}
// l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
var ne *Element
if ne, err = l1.InsertBefore(8, e); err != nil {
t.Errorf("l1.InsertBefore(8, e) = %v, want nil", err)
}
//goland:noinspection GoCommentLeadingSpace (lol)
if ne == nil { // nolint:SA5011
t.Fatalf("l1.InsertBefore(8, e) = nil, want non-nil")
}
//goland:noinspection GoCommentLeadingSpace (lol)
if ne.Element == nil { // nolint:SA5011
t.Errorf("l1.InsertBefore(8, e) = nil, want non-nil")
}
if ne.Value() != 8 {
t.Errorf("l1.InsertBefore(8, e) = %v, want 8", ne.Value())
}
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
}
// Adapted from golang source
func TestIssue6349(t *testing.T) {
t.Parallel()
l := New()
l.PushBack(1)
l.PushBack(2)
e := l.Front()
if e.Value() != 1 {
t.Errorf("e.value = %d, want 1", e.Value())
}
if err := l.Remove(e); err != nil {
t.Errorf("l.Remove(e) = %v, want nil", err)
}
if e.Next() != nil {
t.Errorf("e.Next() != nil")
}
if e.Prev() != nil {
t.Errorf("e.Prev() != nil")
}
}
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
// Adapted from golang source.
func TestZeroList(t *testing.T) {
t.Parallel()
var l1 = new(LockingList)
l1.PushFront(1)
checkList(t, l1, []any{1})
var l2 = new(LockingList)
l2.PushBack(1)
checkList(t, l2, []any{1})
var l3 = new(LockingList)
if err := l3.PushFrontList(l1); err != nil {
t.Errorf("l3.PushFrontList(l1) = %v, want nil", err)
}
checkList(t, l3, []any{1})
var l4 = new(LockingList)
if err := l4.PushBackList(l2); err != nil {
t.Errorf("l4.PushBackList(l2) = %v, want nil", err)
}
checkList(t, l4, []any{1})
}
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
// Adapted from golang source.
func TestInsertBeforeUnknownMark(t *testing.T) {
t.Parallel()
var l = New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
_, err := l.InsertBefore(1, new(Element))
if err == nil || !errors.Is(err, ErrMarkNotInList) {
t.Errorf("l.InsertBefore(1, new(Element)) = %v, want ErrMarkNotInList", err)
}
checkList(t, l, []any{1, 2, 3})
}
// TestList tests the list implementation.
// Mostly adapted from golang source
func TestList(t *testing.T) {
l := New()
checkListPointers(t, l, []*list.Element{})
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*list.Element{e.Element})
if err := l.MoveToFront(e); err != nil {
t.Errorf("MoveToFront(e) = %v, want nil", err)
}
if err := l.MoveAfter(e, e); err != nil {
t.Errorf("MoveAfter(e, e) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e.Element})
if err := l.MoveToBack(e); err != nil {
t.Errorf("MoveToBack(e) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e.Element})
if err := l.Remove(e); err != nil {
t.Errorf("Remove(e) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*list.Element{e1.Element, e2.Element, e3.Element, e4.Element})
var err error
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e3.Element, e4.Element})
// move from middle
if err = l.MoveToFront(e3); err != nil {
t.Errorf("MoveToFront(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e3.Element, e1.Element, e4.Element})
if err = l.MoveToFront(e1); err != nil {
t.Errorf("MoveToFront(e1) = %v, want nil", err)
}
// move from middle
if err = l.MoveToBack(e3); err != nil {
t.Errorf("MoveToBack(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e3.Element})
// move from back
if err = l.MoveToFront(e3); err != nil {
t.Errorf("MoveToFront(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e3.Element, e1.Element, e4.Element})
// should be no-op
if err = l.MoveToFront(e3); err != nil {
t.Errorf("MoveToFront(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e3.Element, e1.Element, e4.Element})
// move from front
if err = l.MoveToBack(e3); err != nil {
t.Errorf("MoveToBack(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e3.Element})
// should be no-op
if err = l.MoveToBack(e3); err != nil {
t.Errorf("MoveToBack(e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e3.Element})
// insert before front
if e2, err = l.InsertBefore(2, e1); err != nil {
t.Errorf("InsertBefore(2, e1) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e2.Element, e1.Element, e4.Element, e3.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// insert before middle
if e2, err = l.InsertBefore(2, e4); err != nil {
t.Errorf("InsertBefore(2, e4) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e2.Element, e4.Element, e3.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// insert before back
if e2, err = l.InsertBefore(2, e3); err != nil {
t.Errorf("InsertBefore(2, e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e2.Element, e3.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// insert after front
if e2, err = l.InsertAfter(2, e1); err != nil {
t.Errorf("InsertAfter(2, e1) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e2.Element, e4.Element, e3.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// insert after middle
if e2, err = l.InsertAfter(2, e4); err != nil {
t.Errorf("InsertAfter(2, e4) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e2.Element, e3.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// insert after back
if e2, err = l.InsertAfter(2, e3); err != nil {
t.Errorf("InsertAfter(2, e3) = %v, want nil", err)
}
checkListPointers(t, l, []*list.Element{e1.Element, e4.Element, e3.Element, e2.Element})
if err = l.Remove(e2); err != nil {
t.Errorf("Remove(e2) = %v, want nil", err)
}
// Check standard iteration.
sum := 0
for e = l.Front(); e != nil; e = e.Next() {
if i, ok := e.Element.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
}
func TestThePlanet(t *testing.T) {
t.Parallel()
var err error
var nl = New()
if nl.Len() != 0 {
t.Errorf("Init() failed to reset list length to 0")
}
nl.PushFront(1)
if nl.Pop() != 1 {
t.Errorf("Pop() failed to return first element")
}
if nl.Len() != 0 {
t.Errorf("Pop() failed to remove first element")
}
if err = nl.Push(1); err != nil {
t.Errorf("Push(1) = %v, want nil", err)
}
nl.l = nil
if err = nl.Push(1); err != nil {
t.Errorf("Push(1) = %v, want %v", err, nil)
}
if nl.Pop() != 1 {
t.Errorf("Pop() = %v, want %v", err, 1)
}
if nl.Pop() != nil {
t.Errorf("Pop() = %v, want %v", err, nil)
}
t.Run("PushPop", func(t *testing.T) {
t.Parallel()
pl := New()
for i := 1; i < 5; i++ {
if err = pl.Push(i); err != nil {
t.Errorf("Push(%d) = %v, want nil", i, err)
}
}
for i := 4; i > 0; i-- {
if pl.Pop() != i {
t.Errorf("Pop() = %d, want %d", pl.Pop(), i)
}
}
for i := 1; i < 5; i++ {
if err = pl.Push(i); err != nil {
t.Errorf("Push(%d) = %v, want nil", i, err)
}
}
})
t.Run("Concurrent", func(t *testing.T) {
t.Parallel()
cl := New()
wg := &sync.WaitGroup{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func(n int) {
if cerr := cl.Push(n); cerr != nil {
t.Errorf("Push(%d) err = %v, want nil", i, cerr)
}
wg.Done()
}(i)
}
wg.Wait()
if cl.Len() != 1000 {
t.Errorf("Len() = %d, want 1000", cl.Len())
}
for i := 0; i < 1000; i++ {
if cl.Pop() == nil {
t.Errorf("Pop() = %d, want %d", cl.Pop(), i)
}
if cl.Len() != 999-i {
t.Errorf("Len() = %d, want %d", cl.Len(), 999-i)
}
}
})
nl.Init()
for i := 1; i < 5; i++ {
if err = nl.Push(i); err != nil {
t.Errorf("Push(%d) = %v, want nil", i, err)
}
}
for i := 1; i < 100; i++ {
for j := 1; j < 5; j++ {
res := nl.Rotate()
t.Logf("Rotate() = %d", res.Value())
if res.Value() != j {
t.Errorf("Rotate() = %d, want %d", res.Value(), j)
}
if j > 4 && res.Next() == nil {
t.Fatalf("%d.Next is nil", res.Value())
}
if j < 2 && res.Prev() == nil {
t.Errorf("Prev is nil")
}
if res.Next() != nil && res.Next().Value() != j+1 {
t.Errorf("Next = %d, want %d", res.Next().Value(), j+1)
}
if res.Prev() != nil && j-1 != 0 && res.Prev().Value() != j-1 {
t.Errorf("Prev = %d, want %d", res.Prev().Value(), j-1)
}
}
}
t.Run("CoverageStuff", func(t *testing.T) {
nl.RWMutex = nil
if !errors.Is(nl.Lock(), ErrUninitialized) {
t.Errorf("Lock() = %v, want %v", err, ErrUninitialized)
}
var yeet *Element
if yeet, err = nl.InsertAfter(1, nil); !errors.Is(err, ErrUninitialized) {
t.Errorf("InsertAfter(1, nil) = %v, want %v", err, ErrUninitialized)
}
if yeet.Next() != nil {
t.Errorf("Next() = %v, want %v", yeet.Next(), nil)
}
if yeet.Prev() != nil {
t.Errorf("Prev() = %v, want %v", yeet.Prev(), nil)
}
if _, err = nl.InsertBefore(1, nil); !errors.Is(err, ErrUninitialized) {
t.Errorf("InsertAfter(1, nil) = %v, want %v", err, ErrUninitialized)
}
if err = nl.Remove(nil); !errors.Is(err, ErrUninitialized) {
t.Errorf("Remove(nil) = %v, want %v", err, ErrUninitialized)
}
if err = nl.Push(1); !errors.Is(err, ErrUninitialized) {
t.Errorf("Push(1) = %v, want %v", err, ErrUninitialized)
}
if e := nl.PushFront(1); e != nil {
t.Errorf("PushFront(1) = %v, want %v", err, ErrUninitialized)
}
nl.l = nil
nl.Rotate()
if nl.wrapElement(nil) != nil {
t.Errorf("wrapElement(e) = %v, want nil", err)
}
if nl.wrapElement(nil).Value() != nil {
t.Errorf("wrapElement(e).Value() = %v, want nil", err)
}
el := &Element{}
if el.Value() != nil {
t.Errorf("el.Value() = %v, want nil", err)
}
if _, err = nl.InsertAfter(1, nil); err == nil {
t.Errorf("InsertAfter(1, nil) = %v, want error", err)
}
nl.Init()
nl.Init()
nl.l = nil
if !errors.Is(nl.Remove(nil), ErrUninitialized) {
t.Errorf("Remove(nil) = %v, want %v", err, ErrUninitialized)
}
nl.Init()
_ = nl.Push(1)
_ = nl.Push(2)
if nl.Back().Value() == 1 {
t.Errorf("Back() = %v, want %v", err, 1)
}
})
// Clear all elements by iterating
for nl.Len() > 0 {
nl.Pop()
}
checkListPointers(t, nl, []*list.Element{})
}