mirror of https://github.com/restic/restic.git
184 lines
3.8 KiB
Go
184 lines
3.8 KiB
Go
|
package tomb_test
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"gopkg.in/tomb.v2"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func nothing() error { return nil }
|
||
|
|
||
|
func TestNewTomb(t *testing.T) {
|
||
|
tb := &tomb.Tomb{}
|
||
|
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||
|
}
|
||
|
|
||
|
func TestGo(t *testing.T) {
|
||
|
tb := &tomb.Tomb{}
|
||
|
alive := make(chan bool)
|
||
|
tb.Go(func() error {
|
||
|
alive <- true
|
||
|
tb.Go(func() error {
|
||
|
alive <- true
|
||
|
<-tb.Dying()
|
||
|
return nil
|
||
|
})
|
||
|
<-tb.Dying()
|
||
|
return nil
|
||
|
})
|
||
|
<-alive
|
||
|
<-alive
|
||
|
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||
|
tb.Kill(nil)
|
||
|
tb.Wait()
|
||
|
checkState(t, tb, true, true, nil)
|
||
|
}
|
||
|
|
||
|
func TestGoErr(t *testing.T) {
|
||
|
first := errors.New("first error")
|
||
|
second := errors.New("first error")
|
||
|
tb := &tomb.Tomb{}
|
||
|
alive := make(chan bool)
|
||
|
tb.Go(func() error {
|
||
|
alive <- true
|
||
|
tb.Go(func() error {
|
||
|
alive <- true
|
||
|
return first
|
||
|
})
|
||
|
<-tb.Dying()
|
||
|
return second
|
||
|
})
|
||
|
<-alive
|
||
|
<-alive
|
||
|
tb.Wait()
|
||
|
checkState(t, tb, true, true, first)
|
||
|
}
|
||
|
|
||
|
func TestGoPanic(t *testing.T) {
|
||
|
// ErrDying being used properly, after a clean death.
|
||
|
tb := &tomb.Tomb{}
|
||
|
tb.Go(nothing)
|
||
|
tb.Wait()
|
||
|
defer func() {
|
||
|
err := recover()
|
||
|
if err != "tomb.Go called after all goroutines terminated" {
|
||
|
t.Fatalf("Wrong panic on post-death tomb.Go call: %v", err)
|
||
|
}
|
||
|
checkState(t, tb, true, true, nil)
|
||
|
}()
|
||
|
tb.Go(nothing)
|
||
|
}
|
||
|
|
||
|
func TestKill(t *testing.T) {
|
||
|
// a nil reason flags the goroutine as dying
|
||
|
tb := &tomb.Tomb{}
|
||
|
tb.Kill(nil)
|
||
|
checkState(t, tb, true, false, nil)
|
||
|
|
||
|
// a non-nil reason now will override Kill
|
||
|
err := errors.New("some error")
|
||
|
tb.Kill(err)
|
||
|
checkState(t, tb, true, false, err)
|
||
|
|
||
|
// another non-nil reason won't replace the first one
|
||
|
tb.Kill(errors.New("ignore me"))
|
||
|
checkState(t, tb, true, false, err)
|
||
|
|
||
|
tb.Go(nothing)
|
||
|
tb.Wait()
|
||
|
checkState(t, tb, true, true, err)
|
||
|
}
|
||
|
|
||
|
func TestKillf(t *testing.T) {
|
||
|
tb := &tomb.Tomb{}
|
||
|
|
||
|
err := tb.Killf("BO%s", "OM")
|
||
|
if s := err.Error(); s != "BOOM" {
|
||
|
t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
|
||
|
}
|
||
|
checkState(t, tb, true, false, err)
|
||
|
|
||
|
// another non-nil reason won't replace the first one
|
||
|
tb.Killf("ignore me")
|
||
|
checkState(t, tb, true, false, err)
|
||
|
|
||
|
tb.Go(nothing)
|
||
|
tb.Wait()
|
||
|
checkState(t, tb, true, true, err)
|
||
|
}
|
||
|
|
||
|
func TestErrDying(t *testing.T) {
|
||
|
// ErrDying being used properly, after a clean death.
|
||
|
tb := &tomb.Tomb{}
|
||
|
tb.Kill(nil)
|
||
|
tb.Kill(tomb.ErrDying)
|
||
|
checkState(t, tb, true, false, nil)
|
||
|
|
||
|
// ErrDying being used properly, after an errorful death.
|
||
|
err := errors.New("some error")
|
||
|
tb.Kill(err)
|
||
|
tb.Kill(tomb.ErrDying)
|
||
|
checkState(t, tb, true, false, err)
|
||
|
|
||
|
// ErrDying being used badly, with an alive tomb.
|
||
|
tb = &tomb.Tomb{}
|
||
|
defer func() {
|
||
|
err := recover()
|
||
|
if err != "tomb: Kill with ErrDying while still alive" {
|
||
|
t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
|
||
|
}
|
||
|
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||
|
}()
|
||
|
tb.Kill(tomb.ErrDying)
|
||
|
}
|
||
|
|
||
|
func TestKillErrStillAlivePanic(t *testing.T) {
|
||
|
tb := &tomb.Tomb{}
|
||
|
defer func() {
|
||
|
err := recover()
|
||
|
if err != "tomb: Kill with ErrStillAlive" {
|
||
|
t.Fatalf("Wrong panic on Kill(ErrStillAlive): %v", err)
|
||
|
}
|
||
|
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||
|
}()
|
||
|
tb.Kill(tomb.ErrStillAlive)
|
||
|
}
|
||
|
|
||
|
func checkState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
|
||
|
select {
|
||
|
case <-tb.Dying():
|
||
|
if !wantDying {
|
||
|
t.Error("<-Dying: should block")
|
||
|
}
|
||
|
default:
|
||
|
if wantDying {
|
||
|
t.Error("<-Dying: should not block")
|
||
|
}
|
||
|
}
|
||
|
seemsDead := false
|
||
|
select {
|
||
|
case <-tb.Dead():
|
||
|
if !wantDead {
|
||
|
t.Error("<-Dead: should block")
|
||
|
}
|
||
|
seemsDead = true
|
||
|
default:
|
||
|
if wantDead {
|
||
|
t.Error("<-Dead: should not block")
|
||
|
}
|
||
|
}
|
||
|
if err := tb.Err(); err != wantErr {
|
||
|
t.Errorf("Err: want %#v, got %#v", wantErr, err)
|
||
|
}
|
||
|
if wantDead && seemsDead {
|
||
|
waitErr := tb.Wait()
|
||
|
switch {
|
||
|
case waitErr == tomb.ErrStillAlive:
|
||
|
t.Errorf("Wait should not return ErrStillAlive")
|
||
|
case !reflect.DeepEqual(waitErr, wantErr):
|
||
|
t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
|
||
|
}
|
||
|
}
|
||
|
}
|