go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/sync/promise/promise_test.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package promise 16 17 import ( 18 "context" 19 "errors" 20 "testing" 21 "time" 22 23 . "github.com/smartystreets/goconvey/convey" 24 25 "go.chromium.org/luci/common/clock" 26 "go.chromium.org/luci/common/clock/testclock" 27 ) 28 29 func TestPromise(t *testing.T) { 30 t.Parallel() 31 32 Convey(`An instrumented Promise instance`, t, func() { 33 ctx, tc := testclock.UseTime(context.Background(), time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)) 34 35 type operation struct { 36 d any 37 e error 38 } 39 40 opC := make(chan operation) 41 p := New(ctx, func(context.Context) (any, error) { 42 op := <-opC 43 return op.d, op.e 44 }) 45 46 Convey(`Has no data by default.`, func() { 47 data, err := p.Peek() 48 So(data, ShouldBeNil) 49 So(err, ShouldEqual, ErrNoData) 50 }) 51 52 Convey(`Will timeout with no data.`, func() { 53 // Wait until our Promise starts its timer. Then signal it. 54 readyC := make(chan struct{}) 55 tc.SetTimerCallback(func(_ time.Duration, _ clock.Timer) { 56 close(readyC) 57 }) 58 go func() { 59 <-readyC 60 tc.Add(1 * time.Second) 61 }() 62 63 ctx, cancel := clock.WithTimeout(ctx, 1*time.Second) 64 defer cancel() 65 data, err := p.Get(ctx) 66 So(data, ShouldBeNil) 67 So(err.Error(), ShouldEqual, context.DeadlineExceeded.Error()) 68 }) 69 70 Convey(`With data already added`, func() { 71 e := errors.New("promise: fake test error") 72 opC <- operation{"DATA", e} 73 74 data, err := p.Get(ctx) 75 So(data, ShouldEqual, "DATA") 76 So(err, ShouldEqual, e) 77 78 Convey(`Will return data immediately.`, func() { 79 data, err = p.Peek() 80 So(data, ShouldEqual, "DATA") 81 So(err, ShouldEqual, e) 82 }) 83 84 Convey(`Will return data instead of timing out.`, func() { 85 ctx, cancelFunc := context.WithCancel(ctx) 86 cancelFunc() 87 88 data, err = p.Get(ctx) 89 So(data, ShouldEqual, "DATA") 90 So(err, ShouldEqual, e) 91 }) 92 }) 93 }) 94 } 95 96 func TestDeferredPromise(t *testing.T) { 97 t.Parallel() 98 99 Convey(`A deferred Promise instance`, t, func() { 100 c := context.Background() 101 102 Convey(`Will defer running until Get is called, and will panic the Get goroutine.`, func() { 103 // Since our Get will cause the generator to be run in this goroutine, 104 // calling Get with a generator that panics should cause a panic. 105 p := NewDeferred(func(context.Context) (any, error) { panic("test panic") }) 106 So(func() { p.Get(c) }, ShouldPanic) 107 }) 108 109 Convey(`Can output data.`, func() { 110 p := NewDeferred(func(context.Context) (any, error) { 111 return "hello", nil 112 }) 113 v, err := p.Get(c) 114 So(err, ShouldBeNil) 115 So(v, ShouldEqual, "hello") 116 }) 117 }) 118 } 119 120 func TestPromiseSmoke(t *testing.T) { 121 t.Parallel() 122 123 Convey(`A Promise instance with multiple consumers will block.`, t, func() { 124 ctx, _ := testclock.UseTime(context.Background(), time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)) 125 dataC := make(chan any) 126 p := New(ctx, func(context.Context) (any, error) { 127 return <-dataC, nil 128 }) 129 130 finishedC := make(chan string) 131 for i := uint(0); i < 100; i++ { 132 go func() { 133 data, _ := p.Get(ctx) 134 finishedC <- data.(string) 135 }() 136 go func() { 137 ctx, cancel := clock.WithTimeout(ctx, 1*time.Hour) 138 defer cancel() 139 data, _ := p.Get(ctx) 140 finishedC <- data.(string) 141 }() 142 } 143 144 dataC <- "DATA" 145 for i := uint(0); i < 200; i++ { 146 So(<-finishedC, ShouldEqual, "DATA") 147 } 148 }) 149 }