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  }