Source file src/errors/wrap_test.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package errors_test
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io/fs"
    11  	"os"
    12  	"reflect"
    13  	"testing"
    14  )
    15  
    16  func TestIs(t *testing.T) {
    17  	err1 := errors.New("1")
    18  	erra := wrapped{"wrap 2", err1}
    19  	errb := wrapped{"wrap 3", erra}
    20  
    21  	err3 := errors.New("3")
    22  
    23  	poser := &poser{"either 1 or 3", func(err error) bool {
    24  		return err == err1 || err == err3
    25  	}}
    26  
    27  	testCases := []struct {
    28  		err    error
    29  		target error
    30  		match  bool
    31  	}{
    32  		{nil, nil, true},
    33  		{err1, nil, false},
    34  		{err1, err1, true},
    35  		{erra, err1, true},
    36  		{errb, err1, true},
    37  		{err1, err3, false},
    38  		{erra, err3, false},
    39  		{errb, err3, false},
    40  		{poser, err1, true},
    41  		{poser, err3, true},
    42  		{poser, erra, false},
    43  		{poser, errb, false},
    44  		{errorUncomparable{}, errorUncomparable{}, true},
    45  		{errorUncomparable{}, &errorUncomparable{}, false},
    46  		{&errorUncomparable{}, errorUncomparable{}, true},
    47  		{&errorUncomparable{}, &errorUncomparable{}, false},
    48  		{errorUncomparable{}, err1, false},
    49  		{&errorUncomparable{}, err1, false},
    50  	}
    51  	for _, tc := range testCases {
    52  		t.Run("", func(t *testing.T) {
    53  			if got := errors.Is(tc.err, tc.target); got != tc.match {
    54  				t.Errorf("Is(%v, %v) = %v, want %v", tc.err, tc.target, got, tc.match)
    55  			}
    56  		})
    57  	}
    58  }
    59  
    60  type poser struct {
    61  	msg string
    62  	f   func(error) bool
    63  }
    64  
    65  var poserPathErr = &fs.PathError{Op: "poser"}
    66  
    67  func (p *poser) Error() string     { return p.msg }
    68  func (p *poser) Is(err error) bool { return p.f(err) }
    69  func (p *poser) As(err any) bool {
    70  	switch x := err.(type) {
    71  	case **poser:
    72  		*x = p
    73  	case *errorT:
    74  		*x = errorT{"poser"}
    75  	case **fs.PathError:
    76  		*x = poserPathErr
    77  	default:
    78  		return false
    79  	}
    80  	return true
    81  }
    82  
    83  func TestAs(t *testing.T) {
    84  	var errT errorT
    85  	var errP *fs.PathError
    86  	var timeout interface{ Timeout() bool }
    87  	var p *poser
    88  	_, errF := os.Open("non-existing")
    89  	poserErr := &poser{"oh no", nil}
    90  
    91  	testCases := []struct {
    92  		err    error
    93  		target any
    94  		match  bool
    95  		want   any // value of target on match
    96  	}{{
    97  		nil,
    98  		&errP,
    99  		false,
   100  		nil,
   101  	}, {
   102  		wrapped{"pitied the fool", errorT{"T"}},
   103  		&errT,
   104  		true,
   105  		errorT{"T"},
   106  	}, {
   107  		errF,
   108  		&errP,
   109  		true,
   110  		errF,
   111  	}, {
   112  		errorT{},
   113  		&errP,
   114  		false,
   115  		nil,
   116  	}, {
   117  		wrapped{"wrapped", nil},
   118  		&errT,
   119  		false,
   120  		nil,
   121  	}, {
   122  		&poser{"error", nil},
   123  		&errT,
   124  		true,
   125  		errorT{"poser"},
   126  	}, {
   127  		&poser{"path", nil},
   128  		&errP,
   129  		true,
   130  		poserPathErr,
   131  	}, {
   132  		poserErr,
   133  		&p,
   134  		true,
   135  		poserErr,
   136  	}, {
   137  		errors.New("err"),
   138  		&timeout,
   139  		false,
   140  		nil,
   141  	}, {
   142  		errF,
   143  		&timeout,
   144  		true,
   145  		errF,
   146  	}, {
   147  		wrapped{"path error", errF},
   148  		&timeout,
   149  		true,
   150  		errF,
   151  	}}
   152  	for i, tc := range testCases {
   153  		name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target)
   154  		// Clear the target pointer, in case it was set in a previous test.
   155  		rtarget := reflect.ValueOf(tc.target)
   156  		rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem()))
   157  		t.Run(name, func(t *testing.T) {
   158  			match := errors.As(tc.err, tc.target)
   159  			if match != tc.match {
   160  				t.Fatalf("match: got %v; want %v", match, tc.match)
   161  			}
   162  			if !match {
   163  				return
   164  			}
   165  			if got := rtarget.Elem().Interface(); got != tc.want {
   166  				t.Fatalf("got %#v, want %#v", got, tc.want)
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestAsValidation(t *testing.T) {
   173  	var s string
   174  	testCases := []any{
   175  		nil,
   176  		(*int)(nil),
   177  		"error",
   178  		&s,
   179  	}
   180  	err := errors.New("error")
   181  	for _, tc := range testCases {
   182  		t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) {
   183  			defer func() {
   184  				recover()
   185  			}()
   186  			if errors.As(err, tc) {
   187  				t.Errorf("As(err, %T(%v)) = true, want false", tc, tc)
   188  				return
   189  			}
   190  			t.Errorf("As(err, %T(%v)) did not panic", tc, tc)
   191  		})
   192  	}
   193  }
   194  
   195  func TestUnwrap(t *testing.T) {
   196  	err1 := errors.New("1")
   197  	erra := wrapped{"wrap 2", err1}
   198  
   199  	testCases := []struct {
   200  		err  error
   201  		want error
   202  	}{
   203  		{nil, nil},
   204  		{wrapped{"wrapped", nil}, nil},
   205  		{err1, nil},
   206  		{erra, err1},
   207  		{wrapped{"wrap 3", erra}, erra},
   208  	}
   209  	for _, tc := range testCases {
   210  		if got := errors.Unwrap(tc.err); got != tc.want {
   211  			t.Errorf("Unwrap(%v) = %v, want %v", tc.err, got, tc.want)
   212  		}
   213  	}
   214  }
   215  
   216  type errorT struct{ s string }
   217  
   218  func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
   219  
   220  type wrapped struct {
   221  	msg string
   222  	err error
   223  }
   224  
   225  func (e wrapped) Error() string { return e.msg }
   226  
   227  func (e wrapped) Unwrap() error { return e.err }
   228  
   229  type errorUncomparable struct {
   230  	f []string
   231  }
   232  
   233  func (errorUncomparable) Error() string {
   234  	return "uncomparable error"
   235  }
   236  
   237  func (errorUncomparable) Is(target error) bool {
   238  	_, ok := target.(errorUncomparable)
   239  	return ok
   240  }
   241  
   242  func ExampleIs() {
   243  	if _, err := os.Open("non-existing"); err != nil {
   244  		if errors.Is(err, fs.ErrNotExist) {
   245  			fmt.Println("file does not exist")
   246  		} else {
   247  			fmt.Println(err)
   248  		}
   249  	}
   250  
   251  	// Output:
   252  	// file does not exist
   253  }
   254  
   255  func ExampleAs() {
   256  	if _, err := os.Open("non-existing"); err != nil {
   257  		var pathError *fs.PathError
   258  		if errors.As(err, &pathError) {
   259  			fmt.Println("Failed at path:", pathError.Path)
   260  		} else {
   261  			fmt.Println(err)
   262  		}
   263  	}
   264  
   265  	// Output:
   266  	// Failed at path: non-existing
   267  }
   268  
   269  func ExampleUnwrap() {
   270  	err1 := errors.New("error1")
   271  	err2 := fmt.Errorf("error2: [%w]", err1)
   272  	fmt.Println(err2)
   273  	fmt.Println(errors.Unwrap(err2))
   274  	// Output
   275  	// error2: [error1]
   276  	// error1
   277  }
   278  

View as plain text