github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/internal/syncutil/once_test.go (about) 1 /* 2 Copyright The ORAS Authors. 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 16 package syncutil 17 18 import ( 19 "context" 20 "errors" 21 "io" 22 "reflect" 23 "strconv" 24 "sync" 25 "testing" 26 "time" 27 ) 28 29 func TestOnce_Do(t *testing.T) { 30 var f []func() (interface{}, error) 31 for i := 0; i < 100; i++ { 32 f = append(f, func(i int) func() (interface{}, error) { 33 return func() (interface{}, error) { 34 return i + 1, errors.New(strconv.Itoa(i)) 35 } 36 }(i)) 37 } 38 39 once := NewOnce() 40 first := make([]bool, len(f)) 41 result := make([]interface{}, len(f)) 42 err := make([]error, len(f)) 43 var wg sync.WaitGroup 44 for i := 0; i < len(f); i++ { 45 wg.Add(1) 46 go func(i int) { 47 defer wg.Done() 48 ctx := context.Background() 49 first[i], result[i], err[i] = once.Do(ctx, f[i]) 50 }(i) 51 } 52 wg.Wait() 53 54 target := 0 55 for i := 0; i < len(f); i++ { 56 if first[i] { 57 target = i 58 break 59 } 60 } 61 targetErr := err[target] 62 if targetErr == nil || targetErr.Error() != strconv.Itoa(target) { 63 t.Errorf("Once.Do(%d) error = %v, wantErr %v", target, targetErr, strconv.Itoa(target)) 64 } 65 66 wantResult := target + 1 67 wantErr := targetErr 68 for i := 0; i < len(f); i++ { 69 wantFirst := false 70 if i == target { 71 wantFirst = true 72 } 73 if first[i] != wantFirst { 74 t.Errorf("Once.Do(%d) first = %v, want %v", i, first[i], wantFirst) 75 } 76 if err[i] != wantErr { 77 t.Errorf("Once.Do(%d) error = %v, wantErr %v", i, err[i], wantErr) 78 } 79 if !reflect.DeepEqual(result[i], wantResult) { 80 t.Errorf("Once.Do(%d) result = %v, want %v", i, result[i], wantResult) 81 } 82 } 83 } 84 85 func TestOnce_Do_Cancel_Context(t *testing.T) { 86 once := NewOnce() 87 88 var wg sync.WaitGroup 89 var ( 90 first bool 91 result interface{} 92 err error 93 ) 94 wg.Add(1) 95 go func() { 96 defer wg.Done() 97 ctx := context.Background() 98 first, result, err = once.Do(ctx, func() (interface{}, error) { 99 time.Sleep(200 * time.Millisecond) 100 return "foo", io.EOF 101 }) 102 }() 103 time.Sleep(100 * time.Millisecond) 104 ctx := context.Background() 105 ctx, cancel := context.WithCancel(ctx) 106 cancel() 107 first2, result2, err2 := once.Do(ctx, func() (interface{}, error) { 108 return "bar", nil 109 }) 110 wg.Wait() 111 112 if wantFirst := true; first != wantFirst { 113 t.Fatalf("Once.Do() first = %v, want %v", first, wantFirst) 114 } 115 if wantErr := io.EOF; err != wantErr { 116 t.Fatalf("Once.Do() error = %v, wantErr %v", err, wantErr) 117 } 118 if wantResult := "foo"; !reflect.DeepEqual(result, wantResult) { 119 t.Fatalf("Once.Do() result = %v, want %v", result, wantResult) 120 } 121 122 if wantFirst := false; first2 != wantFirst { 123 t.Fatalf("Once.Do() first = %v, want %v", first2, wantFirst) 124 } 125 if wantErr := context.Canceled; err2 != wantErr { 126 t.Fatalf("Once.Do() error = %v, wantErr %v", err2, wantErr) 127 } 128 if wantResult := interface{}(nil); !reflect.DeepEqual(result2, wantResult) { 129 t.Fatalf("Once.Do() result = %v, want %v", result2, wantResult) 130 } 131 } 132 133 func TestOnce_Do_Cancel_Function(t *testing.T) { 134 ctx := context.Background() 135 once := NewOnce() 136 137 first, result, err := once.Do(ctx, func() (interface{}, error) { 138 return "foo", context.Canceled 139 }) 140 if wantFirst := false; first != wantFirst { 141 t.Fatalf("Once.Do() first = %v, want %v", first, wantFirst) 142 } 143 if wantErr := context.Canceled; err != wantErr { 144 t.Fatalf("Once.Do() error = %v, wantErr %v", err, wantErr) 145 } 146 if wantResult := interface{}(nil); !reflect.DeepEqual(result, wantResult) { 147 t.Fatalf("Once.Do() result = %v, want %v", result, wantResult) 148 } 149 150 first, result, err = once.Do(ctx, func() (interface{}, error) { 151 return "bar", io.EOF 152 }) 153 if wantFirst := true; first != wantFirst { 154 t.Fatalf("Once.Do() first = %v, want %v", first, wantFirst) 155 } 156 if wantErr := io.EOF; err != wantErr { 157 t.Fatalf("Once.Do() error = %v, wantErr %v", err, wantErr) 158 } 159 if wantResult := "bar"; !reflect.DeepEqual(result, wantResult) { 160 t.Fatalf("Once.Do() result = %v, want %v", result, wantResult) 161 } 162 } 163 164 func TestOnce_Do_Cancel_Panic(t *testing.T) { 165 ctx := context.Background() 166 once := NewOnce() 167 168 func() { 169 defer func() { 170 got := recover() 171 want := "foo" 172 if got != want { 173 t.Fatalf("Once.Do() panic = %v, want %v", got, want) 174 } 175 }() 176 once.Do(ctx, func() (interface{}, error) { 177 panic("foo") 178 }) 179 }() 180 181 first, result, err := once.Do(ctx, func() (interface{}, error) { 182 return "bar", io.EOF 183 }) 184 if wantFirst := true; first != wantFirst { 185 t.Fatalf("Once.Do() first = %v, want %v", first, wantFirst) 186 } 187 if wantErr := io.EOF; err != wantErr { 188 t.Fatalf("Once.Do() error = %v, wantErr %v", err, wantErr) 189 } 190 if wantResult := "bar"; !reflect.DeepEqual(result, wantResult) { 191 t.Fatalf("Once.Do() result = %v, want %v", result, wantResult) 192 } 193 }