1package tomb_test
2
3import (
4	"errors"
5	"gopkg.in/tomb.v1"
6	"reflect"
7	"testing"
8)
9
10func TestNewTomb(t *testing.T) {
11	tb := &tomb.Tomb{}
12	testState(t, tb, false, false, tomb.ErrStillAlive)
13
14	tb.Done()
15	testState(t, tb, true, true, nil)
16}
17
18func TestKill(t *testing.T) {
19	// a nil reason flags the goroutine as dying
20	tb := &tomb.Tomb{}
21	tb.Kill(nil)
22	testState(t, tb, true, false, nil)
23
24	// a non-nil reason now will override Kill
25	err := errors.New("some error")
26	tb.Kill(err)
27	testState(t, tb, true, false, err)
28
29	// another non-nil reason won't replace the first one
30	tb.Kill(errors.New("ignore me"))
31	testState(t, tb, true, false, err)
32
33	tb.Done()
34	testState(t, tb, true, true, err)
35}
36
37func TestKillf(t *testing.T) {
38	tb := &tomb.Tomb{}
39
40	err := tb.Killf("BO%s", "OM")
41	if s := err.Error(); s != "BOOM" {
42		t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
43	}
44	testState(t, tb, true, false, err)
45
46	// another non-nil reason won't replace the first one
47	tb.Killf("ignore me")
48	testState(t, tb, true, false, err)
49
50	tb.Done()
51	testState(t, tb, true, true, err)
52}
53
54func TestErrDying(t *testing.T) {
55	// ErrDying being used properly, after a clean death.
56	tb := &tomb.Tomb{}
57	tb.Kill(nil)
58	tb.Kill(tomb.ErrDying)
59	testState(t, tb, true, false, nil)
60
61	// ErrDying being used properly, after an errorful death.
62	err := errors.New("some error")
63	tb.Kill(err)
64	tb.Kill(tomb.ErrDying)
65	testState(t, tb, true, false, err)
66
67	// ErrDying being used badly, with an alive tomb.
68	tb = &tomb.Tomb{}
69	defer func() {
70		err := recover()
71		if err != "tomb: Kill with ErrDying while still alive" {
72			t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
73		}
74		testState(t, tb, false, false, tomb.ErrStillAlive)
75	}()
76	tb.Kill(tomb.ErrDying)
77}
78
79func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
80	select {
81	case <-tb.Dying():
82		if !wantDying {
83			t.Error("<-Dying: should block")
84		}
85	default:
86		if wantDying {
87			t.Error("<-Dying: should not block")
88		}
89	}
90	seemsDead := false
91	select {
92	case <-tb.Dead():
93		if !wantDead {
94			t.Error("<-Dead: should block")
95		}
96		seemsDead = true
97	default:
98		if wantDead {
99			t.Error("<-Dead: should not block")
100		}
101	}
102	if err := tb.Err(); err != wantErr {
103		t.Errorf("Err: want %#v, got %#v", wantErr, err)
104	}
105	if wantDead && seemsDead {
106		waitErr := tb.Wait()
107		switch {
108		case waitErr == tomb.ErrStillAlive:
109			t.Errorf("Wait should not return ErrStillAlive")
110		case !reflect.DeepEqual(waitErr, wantErr):
111			t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
112		}
113	}
114}
115