github.com/git-amp/amp-sdk-go@v0.7.5/stdlib/task/task_test.go (about) 1 package task_test 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/git-amp/amp-sdk-go/stdlib/task" 11 "github.com/git-amp/amp-sdk-go/stdlib/testutils" 12 ) 13 14 func spawnN(p task.Context, numGoroutines int, delay time.Duration) { 15 for i := 0; i < numGoroutines; i++ { 16 name := fmt.Sprintf("#%d", i+1) 17 p.Go(name, func(ctx task.Context) { 18 time.Sleep(delay) 19 yoyo := delay 20 fmt.Print(yoyo) 21 }) 22 } 23 } 24 25 func TestCore(t *testing.T) { 26 t.Run("basic idle close", func(t *testing.T) { 27 p, _ := task.Start(&task.Task{ 28 Label: "root", 29 IdleClose: time.Nanosecond, 30 }) 31 32 spawnN(p, 1, 1*time.Second) 33 34 select { 35 case <-time.After(2000 * time.Second): 36 t.Fatal("fail") 37 case <-p.Done(): 38 } 39 }) 40 } 41 42 func TestNestedIdleClose(t *testing.T) { 43 t.Run("nested idle close", func(t *testing.T) { 44 p, _ := task.Start(&task.Task{ 45 Label: "root", 46 IdleClose: time.Nanosecond, 47 }) 48 49 child, _ := p.StartChild(&task.Task{ 50 Label: "child", 51 IdleClose: time.Nanosecond, 52 }) 53 spawnN(child, 10, 1*time.Second) 54 55 select { 56 case <-time.After(2 * time.Second): 57 t.Fatal("fail") 58 case <-p.Done(): 59 } 60 }) 61 } 62 63 func TestIdleCloseWithDelay(t *testing.T) { 64 t.Run("idle close with delay", func(t *testing.T) { 65 p, _ := task.Start(&task.Task{ 66 Label: "root with idle close delay", 67 IdleClose: 2 * time.Second, 68 }) 69 70 select { 71 case <-time.After(3 * time.Second): 72 case <-p.Done(): 73 t.Fatal("ctx exited early") 74 default: 75 } 76 77 spawnN(p, 10, 1*time.Second) 78 79 select { 80 case <-time.After(4 * time.Second): 81 t.Fatal("fail") 82 case <-p.Done(): 83 } 84 85 }) 86 } 87 88 func Test6(t *testing.T) { 89 90 t.Run("close cancels children", func(t *testing.T) { 91 p, _ := task.Start(&task.Task{ 92 Label: "close tester", 93 }) 94 95 child, _ := p.StartChild(&task.Task{ 96 Label: "child", 97 }) 98 99 canceled1 := testutils.NewAwaiter() 100 canceled2 := testutils.NewAwaiter() 101 102 foo1, _ := p.Go("foo1", func(ctx task.Context) { 103 select { 104 case <-ctx.Closing(): 105 canceled1.ItHappened() 106 case <-time.After(5 * time.Second): 107 t.Fatal("context wasn't canceled") 108 } 109 }) 110 111 foo2, _ := child.Go("foo2", func(ctx task.Context) { 112 select { 113 case <-ctx.Closing(): 114 canceled2.ItHappened() 115 case <-time.After(5 * time.Second): 116 t.Fatal("context wasn't canceled") 117 } 118 119 }) 120 121 requireDone(t, p.Done(), false) 122 requireDone(t, child.Done(), false) 123 requireDone(t, foo1.Done(), false) 124 requireDone(t, foo2.Done(), false) 125 126 go p.Close() 127 128 canceled1.AwaitOrFail(t) 129 canceled2.AwaitOrFail(t) 130 131 require.Eventually(t, func() bool { return isDone(t, p.Done()) }, 5*time.Second, 100*time.Millisecond) 132 require.Eventually(t, func() bool { return isDone(t, child.Done()) }, 5*time.Second, 100*time.Millisecond) 133 require.Eventually(t, func() bool { return isDone(t, foo1.Done()) }, 5*time.Second, 100*time.Millisecond) 134 require.Eventually(t, func() bool { return isDone(t, foo2.Done()) }, 5*time.Second, 100*time.Millisecond) 135 }) 136 } 137 138 func requireDone(t *testing.T, chDone <-chan struct{}, done bool) { 139 t.Helper() 140 require.Equal(t, done, isDone(t, chDone)) 141 } 142 143 func isDone(t *testing.T, chDone <-chan struct{}) bool { 144 t.Helper() 145 select { 146 case <-chDone: 147 return true 148 default: 149 return false 150 } 151 }