...

Source file src/go.uber.org/multierr/error_test.go

Documentation: go.uber.org/multierr

     1  // Copyright (c) 2017-2021 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package multierr
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"sync"
    28  	"testing"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  // richFormatError is an error that prints a different output depending on
    35  // whether %v or %+v was used.
    36  type richFormatError struct{}
    37  
    38  func (r richFormatError) Error() string {
    39  	return fmt.Sprint(r)
    40  }
    41  
    42  func (richFormatError) Format(f fmt.State, c rune) {
    43  	if c == 'v' && f.Flag('+') {
    44  		io.WriteString(f, "multiline\nmessage\nwith plus")
    45  	} else {
    46  		io.WriteString(f, "without plus")
    47  	}
    48  }
    49  
    50  func appendN(initial, err error, n int) error {
    51  	errs := initial
    52  	for i := 0; i < n; i++ {
    53  		errs = Append(errs, err)
    54  	}
    55  	return errs
    56  }
    57  
    58  func newMultiErr(errors ...error) error {
    59  	return &multiError{errors: errors}
    60  }
    61  
    62  func TestEvery(t *testing.T) {
    63  	myError1 := errors.New("woeful misfortune")
    64  	myError2 := errors.New("worrisome travesty")
    65  
    66  	for _, tt := range []struct {
    67  		desc       string
    68  		giveErr    error
    69  		giveTarget error
    70  		wantIs     bool
    71  		wantEvery  bool
    72  	}{
    73  		{
    74  			desc:       "all match",
    75  			giveErr:    newMultiErr(myError1, myError1, myError1),
    76  			giveTarget: myError1,
    77  			wantIs:     true,
    78  			wantEvery:  true,
    79  		},
    80  		{
    81  			desc:       "one matches",
    82  			giveErr:    newMultiErr(myError1, myError2),
    83  			giveTarget: myError1,
    84  			wantIs:     true,
    85  			wantEvery:  false,
    86  		},
    87  		{
    88  			desc:       "not multiErrs and non equal",
    89  			giveErr:    myError1,
    90  			giveTarget: myError2,
    91  			wantIs:     false,
    92  			wantEvery:  false,
    93  		},
    94  		{
    95  			desc:       "not multiErrs but equal",
    96  			giveErr:    myError1,
    97  			giveTarget: myError1,
    98  			wantIs:     true,
    99  			wantEvery:  true,
   100  		},
   101  		{
   102  			desc:       "not multiErr w multiErr target",
   103  			giveErr:    myError1,
   104  			giveTarget: newMultiErr(myError1, myError1),
   105  			wantIs:     false,
   106  			wantEvery:  false,
   107  		},
   108  		{
   109  			desc:       "multiErr w multiErr target",
   110  			giveErr:    newMultiErr(myError1, myError1),
   111  			giveTarget: newMultiErr(myError1, myError1),
   112  			wantIs:     false,
   113  			wantEvery:  false,
   114  		},
   115  	} {
   116  		t.Run(tt.desc, func(t *testing.T) {
   117  			assert.Equal(t, tt.wantIs, errors.Is(tt.giveErr, tt.giveTarget))
   118  			assert.Equal(t, tt.wantEvery, Every(tt.giveErr, tt.giveTarget))
   119  		})
   120  	}
   121  }
   122  
   123  func TestCombine(t *testing.T) {
   124  	tests := []struct {
   125  		// Input
   126  		giveErrors []error
   127  
   128  		// Resulting error
   129  		wantError error
   130  
   131  		// %+v and %v string representations
   132  		wantMultiline  string
   133  		wantSingleline string
   134  	}{
   135  		{
   136  			giveErrors: nil,
   137  			wantError:  nil,
   138  		},
   139  		{
   140  			giveErrors: []error{},
   141  			wantError:  nil,
   142  		},
   143  		{
   144  			giveErrors: []error{
   145  				errors.New("foo"),
   146  				nil,
   147  				newMultiErr(
   148  					errors.New("bar"),
   149  				),
   150  				nil,
   151  			},
   152  			wantError: newMultiErr(
   153  				errors.New("foo"),
   154  				errors.New("bar"),
   155  			),
   156  			wantMultiline: "the following errors occurred:\n" +
   157  				" -  foo\n" +
   158  				" -  bar",
   159  			wantSingleline: "foo; bar",
   160  		},
   161  		{
   162  			giveErrors:     []error{nil, nil, errors.New("great sadness"), nil},
   163  			wantError:      errors.New("great sadness"),
   164  			wantMultiline:  "great sadness",
   165  			wantSingleline: "great sadness",
   166  		},
   167  		{
   168  			giveErrors: []error{
   169  				errors.New("foo"),
   170  				newMultiErr(
   171  					errors.New("bar"),
   172  				),
   173  			},
   174  			wantError: newMultiErr(
   175  				errors.New("foo"),
   176  				errors.New("bar"),
   177  			),
   178  			wantMultiline: "the following errors occurred:\n" +
   179  				" -  foo\n" +
   180  				" -  bar",
   181  			wantSingleline: "foo; bar",
   182  		},
   183  		{
   184  			giveErrors:     []error{errors.New("great sadness")},
   185  			wantError:      errors.New("great sadness"),
   186  			wantMultiline:  "great sadness",
   187  			wantSingleline: "great sadness",
   188  		},
   189  		{
   190  			giveErrors: []error{
   191  				errors.New("foo"),
   192  				errors.New("bar"),
   193  			},
   194  			wantError: newMultiErr(
   195  				errors.New("foo"),
   196  				errors.New("bar"),
   197  			),
   198  			wantMultiline: "the following errors occurred:\n" +
   199  				" -  foo\n" +
   200  				" -  bar",
   201  			wantSingleline: "foo; bar",
   202  		},
   203  		{
   204  			giveErrors: []error{
   205  				errors.New("great sadness"),
   206  				errors.New("multi\n  line\nerror message"),
   207  				errors.New("single line error message"),
   208  			},
   209  			wantError: newMultiErr(
   210  				errors.New("great sadness"),
   211  				errors.New("multi\n  line\nerror message"),
   212  				errors.New("single line error message"),
   213  			),
   214  			wantMultiline: "the following errors occurred:\n" +
   215  				" -  great sadness\n" +
   216  				" -  multi\n" +
   217  				"      line\n" +
   218  				"    error message\n" +
   219  				" -  single line error message",
   220  			wantSingleline: "great sadness; " +
   221  				"multi\n  line\nerror message; " +
   222  				"single line error message",
   223  		},
   224  		{
   225  			giveErrors: []error{
   226  				errors.New("foo"),
   227  				newMultiErr(
   228  					errors.New("bar"),
   229  					errors.New("baz"),
   230  				),
   231  				errors.New("qux"),
   232  			},
   233  			wantError: newMultiErr(
   234  				errors.New("foo"),
   235  				errors.New("bar"),
   236  				errors.New("baz"),
   237  				errors.New("qux"),
   238  			),
   239  			wantMultiline: "the following errors occurred:\n" +
   240  				" -  foo\n" +
   241  				" -  bar\n" +
   242  				" -  baz\n" +
   243  				" -  qux",
   244  			wantSingleline: "foo; bar; baz; qux",
   245  		},
   246  		{
   247  			giveErrors: []error{
   248  				errors.New("foo"),
   249  				nil,
   250  				newMultiErr(
   251  					errors.New("bar"),
   252  				),
   253  				nil,
   254  			},
   255  			wantError: newMultiErr(
   256  				errors.New("foo"),
   257  				errors.New("bar"),
   258  			),
   259  			wantMultiline: "the following errors occurred:\n" +
   260  				" -  foo\n" +
   261  				" -  bar",
   262  			wantSingleline: "foo; bar",
   263  		},
   264  		{
   265  			giveErrors: []error{
   266  				errors.New("foo"),
   267  				newMultiErr(
   268  					errors.New("bar"),
   269  				),
   270  			},
   271  			wantError: newMultiErr(
   272  				errors.New("foo"),
   273  				errors.New("bar"),
   274  			),
   275  			wantMultiline: "the following errors occurred:\n" +
   276  				" -  foo\n" +
   277  				" -  bar",
   278  			wantSingleline: "foo; bar",
   279  		},
   280  		{
   281  			giveErrors: []error{
   282  				errors.New("foo"),
   283  				richFormatError{},
   284  				errors.New("bar"),
   285  			},
   286  			wantError: newMultiErr(
   287  				errors.New("foo"),
   288  				richFormatError{},
   289  				errors.New("bar"),
   290  			),
   291  			wantMultiline: "the following errors occurred:\n" +
   292  				" -  foo\n" +
   293  				" -  multiline\n" +
   294  				"    message\n" +
   295  				"    with plus\n" +
   296  				" -  bar",
   297  			wantSingleline: "foo; without plus; bar",
   298  		},
   299  	}
   300  
   301  	for i, tt := range tests {
   302  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   303  			err := Combine(tt.giveErrors...)
   304  			require.Equal(t, tt.wantError, err)
   305  
   306  			if tt.wantMultiline != "" {
   307  				t.Run("Sprintf/multiline", func(t *testing.T) {
   308  					assert.Equal(t, tt.wantMultiline, fmt.Sprintf("%+v", err))
   309  				})
   310  			}
   311  
   312  			if tt.wantSingleline != "" {
   313  				t.Run("Sprintf/singleline", func(t *testing.T) {
   314  					assert.Equal(t, tt.wantSingleline, fmt.Sprintf("%v", err))
   315  				})
   316  
   317  				t.Run("Error()", func(t *testing.T) {
   318  					assert.Equal(t, tt.wantSingleline, err.Error())
   319  				})
   320  
   321  				if s, ok := err.(fmt.Stringer); ok {
   322  					t.Run("String()", func(t *testing.T) {
   323  						assert.Equal(t, tt.wantSingleline, s.String())
   324  					})
   325  				}
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  func TestCombineDoesNotModifySlice(t *testing.T) {
   332  	errors := []error{
   333  		errors.New("foo"),
   334  		nil,
   335  		errors.New("bar"),
   336  	}
   337  
   338  	assert.NotNil(t, Combine(errors...))
   339  	assert.Len(t, errors, 3)
   340  	assert.Nil(t, errors[1], 3)
   341  }
   342  
   343  func TestCombineGoodCaseNoAlloc(t *testing.T) {
   344  	errs := make([]error, 10)
   345  	allocs := testing.AllocsPerRun(100, func() {
   346  		Combine(errs...)
   347  	})
   348  	assert.Equal(t, 0.0, allocs)
   349  }
   350  
   351  func TestAppend(t *testing.T) {
   352  	tests := []struct {
   353  		left  error
   354  		right error
   355  		want  error
   356  	}{
   357  		{
   358  			left:  nil,
   359  			right: nil,
   360  			want:  nil,
   361  		},
   362  		{
   363  			left:  nil,
   364  			right: errors.New("great sadness"),
   365  			want:  errors.New("great sadness"),
   366  		},
   367  		{
   368  			left:  errors.New("great sadness"),
   369  			right: nil,
   370  			want:  errors.New("great sadness"),
   371  		},
   372  		{
   373  			left:  errors.New("foo"),
   374  			right: errors.New("bar"),
   375  			want: newMultiErr(
   376  				errors.New("foo"),
   377  				errors.New("bar"),
   378  			),
   379  		},
   380  		{
   381  			left: newMultiErr(
   382  				errors.New("foo"),
   383  				errors.New("bar"),
   384  			),
   385  			right: errors.New("baz"),
   386  			want: newMultiErr(
   387  				errors.New("foo"),
   388  				errors.New("bar"),
   389  				errors.New("baz"),
   390  			),
   391  		},
   392  		{
   393  			left: errors.New("baz"),
   394  			right: newMultiErr(
   395  				errors.New("foo"),
   396  				errors.New("bar"),
   397  			),
   398  			want: newMultiErr(
   399  				errors.New("baz"),
   400  				errors.New("foo"),
   401  				errors.New("bar"),
   402  			),
   403  		},
   404  		{
   405  			left: newMultiErr(
   406  				errors.New("foo"),
   407  			),
   408  			right: newMultiErr(
   409  				errors.New("bar"),
   410  			),
   411  			want: newMultiErr(
   412  				errors.New("foo"),
   413  				errors.New("bar"),
   414  			),
   415  		},
   416  	}
   417  
   418  	for _, tt := range tests {
   419  		assert.Equal(t, tt.want, Append(tt.left, tt.right))
   420  	}
   421  }
   422  
   423  type notMultiErr struct{}
   424  
   425  var _ errorGroup = notMultiErr{}
   426  
   427  func (notMultiErr) Error() string {
   428  	return "great sadness"
   429  }
   430  
   431  func (notMultiErr) Errors() []error {
   432  	return []error{errors.New("great sadness")}
   433  }
   434  
   435  func TestErrors(t *testing.T) {
   436  	tests := []struct {
   437  		give error
   438  		want []error
   439  
   440  		// Don't attempt to cast to errorGroup or *multiError
   441  		dontCast bool
   442  	}{
   443  		{dontCast: true}, // nil
   444  		{
   445  			give:     errors.New("hi"),
   446  			want:     []error{errors.New("hi")},
   447  			dontCast: true,
   448  		},
   449  		{
   450  			// We don't yet support non-multierr errors that do
   451  			// not implement Unwrap() []error.
   452  			give:     notMultiErr{},
   453  			want:     []error{notMultiErr{}},
   454  			dontCast: true,
   455  		},
   456  		{
   457  			give: Combine(
   458  				errors.New("foo"),
   459  				errors.New("bar"),
   460  			),
   461  			want: []error{
   462  				errors.New("foo"),
   463  				errors.New("bar"),
   464  			},
   465  		},
   466  		{
   467  			give: Append(
   468  				errors.New("foo"),
   469  				errors.New("bar"),
   470  			),
   471  			want: []error{
   472  				errors.New("foo"),
   473  				errors.New("bar"),
   474  			},
   475  		},
   476  		{
   477  			give: Append(
   478  				errors.New("foo"),
   479  				Combine(
   480  					errors.New("bar"),
   481  				),
   482  			),
   483  			want: []error{
   484  				errors.New("foo"),
   485  				errors.New("bar"),
   486  			},
   487  		},
   488  		{
   489  			give: Combine(
   490  				errors.New("foo"),
   491  				Append(
   492  					errors.New("bar"),
   493  					errors.New("baz"),
   494  				),
   495  				errors.New("qux"),
   496  			),
   497  			want: []error{
   498  				errors.New("foo"),
   499  				errors.New("bar"),
   500  				errors.New("baz"),
   501  				errors.New("qux"),
   502  			},
   503  		},
   504  	}
   505  
   506  	for i, tt := range tests {
   507  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   508  			t.Run("Errors()", func(t *testing.T) {
   509  				require.Equal(t, tt.want, Errors(tt.give))
   510  			})
   511  
   512  			if tt.dontCast {
   513  				return
   514  			}
   515  
   516  			t.Run("multiError", func(t *testing.T) {
   517  				require.Equal(t, tt.want, tt.give.(*multiError).Errors())
   518  			})
   519  
   520  			t.Run("errorGroup", func(t *testing.T) {
   521  				require.Equal(t, tt.want, tt.give.(errorGroup).Errors())
   522  			})
   523  		})
   524  	}
   525  }
   526  
   527  func createMultiErrWithCapacity() error {
   528  	// Create a multiError that has capacity for more errors so Append will
   529  	// modify the underlying array that may be shared.
   530  	return appendN(nil, errors.New("append"), 50)
   531  }
   532  
   533  func TestAppendDoesNotModify(t *testing.T) {
   534  	initial := createMultiErrWithCapacity()
   535  	err1 := Append(initial, errors.New("err1"))
   536  	err2 := Append(initial, errors.New("err2"))
   537  
   538  	// Make sure the error messages match, since we do modify the copyNeeded
   539  	// atomic, the values cannot be compared.
   540  	assert.EqualError(t, initial, createMultiErrWithCapacity().Error(), "Initial should not be modified")
   541  
   542  	assert.EqualError(t, err1, Append(createMultiErrWithCapacity(), errors.New("err1")).Error())
   543  	assert.EqualError(t, err2, Append(createMultiErrWithCapacity(), errors.New("err2")).Error())
   544  }
   545  
   546  func TestAppendRace(t *testing.T) {
   547  	initial := createMultiErrWithCapacity()
   548  
   549  	var wg sync.WaitGroup
   550  	for i := 0; i < 10; i++ {
   551  		wg.Add(1)
   552  		go func() {
   553  			defer wg.Done()
   554  
   555  			err := initial
   556  			for j := 0; j < 10; j++ {
   557  				err = Append(err, errors.New("err"))
   558  			}
   559  		}()
   560  	}
   561  
   562  	wg.Wait()
   563  }
   564  
   565  func TestErrorsSliceIsImmutable(t *testing.T) {
   566  	err1 := errors.New("err1")
   567  	err2 := errors.New("err2")
   568  
   569  	err := Append(err1, err2)
   570  	gotErrors := Errors(err)
   571  	require.Equal(t, []error{err1, err2}, gotErrors, "errors must match")
   572  
   573  	gotErrors[0] = nil
   574  	gotErrors[1] = errors.New("err3")
   575  
   576  	require.Equal(t, []error{err1, err2}, Errors(err),
   577  		"errors must match after modification")
   578  }
   579  
   580  func TestNilMultierror(t *testing.T) {
   581  	// For safety, all operations on multiError should be safe even if it is
   582  	// nil.
   583  	var err *multiError
   584  
   585  	require.Empty(t, err.Error())
   586  	require.Empty(t, err.Errors())
   587  }
   588  
   589  func TestAppendInto(t *testing.T) {
   590  	tests := []struct {
   591  		desc string
   592  		into *error
   593  		give error
   594  		want error
   595  	}{
   596  		{
   597  			desc: "append into empty",
   598  			into: new(error),
   599  			give: errors.New("foo"),
   600  			want: errors.New("foo"),
   601  		},
   602  		{
   603  			desc: "append into non-empty, non-multierr",
   604  			into: errorPtr(errors.New("foo")),
   605  			give: errors.New("bar"),
   606  			want: Combine(
   607  				errors.New("foo"),
   608  				errors.New("bar"),
   609  			),
   610  		},
   611  		{
   612  			desc: "append into non-empty multierr",
   613  			into: errorPtr(Combine(
   614  				errors.New("foo"),
   615  				errors.New("bar"),
   616  			)),
   617  			give: errors.New("baz"),
   618  			want: Combine(
   619  				errors.New("foo"),
   620  				errors.New("bar"),
   621  				errors.New("baz"),
   622  			),
   623  		},
   624  	}
   625  
   626  	for _, tt := range tests {
   627  		t.Run(tt.desc, func(t *testing.T) {
   628  			assert.True(t, AppendInto(tt.into, tt.give))
   629  			assert.Equal(t, tt.want, *tt.into)
   630  		})
   631  	}
   632  }
   633  
   634  func TestAppendInvoke(t *testing.T) {
   635  	tests := []struct {
   636  		desc string
   637  		into *error
   638  		give Invoker
   639  		want error
   640  	}{
   641  		{
   642  			desc: "append into empty",
   643  			into: new(error),
   644  			give: Invoke(func() error {
   645  				return errors.New("foo")
   646  			}),
   647  			want: errors.New("foo"),
   648  		},
   649  		{
   650  			desc: "append into non-empty, non-multierr",
   651  			into: errorPtr(errors.New("foo")),
   652  			give: Invoke(func() error {
   653  				return errors.New("bar")
   654  			}),
   655  			want: Combine(
   656  				errors.New("foo"),
   657  				errors.New("bar"),
   658  			),
   659  		},
   660  		{
   661  			desc: "append into non-empty multierr",
   662  			into: errorPtr(Combine(
   663  				errors.New("foo"),
   664  				errors.New("bar"),
   665  			)),
   666  			give: Invoke(func() error {
   667  				return errors.New("baz")
   668  			}),
   669  			want: Combine(
   670  				errors.New("foo"),
   671  				errors.New("bar"),
   672  				errors.New("baz"),
   673  			),
   674  		},
   675  		{
   676  			desc: "close/empty",
   677  			into: new(error),
   678  			give: Close(newCloserMock(t, errors.New("foo"))),
   679  			want: errors.New("foo"),
   680  		},
   681  		{
   682  			desc: "close/no fail",
   683  			into: new(error),
   684  			give: Close(newCloserMock(t, nil)),
   685  			want: nil,
   686  		},
   687  		{
   688  			desc: "close/non-empty",
   689  			into: errorPtr(errors.New("foo")),
   690  			give: Close(newCloserMock(t, errors.New("bar"))),
   691  			want: Combine(
   692  				errors.New("foo"),
   693  				errors.New("bar"),
   694  			),
   695  		},
   696  	}
   697  	for _, tt := range tests {
   698  		t.Run(tt.desc, func(t *testing.T) {
   699  			AppendInvoke(tt.into, tt.give)
   700  			assert.Equal(t, tt.want, *tt.into)
   701  		})
   702  	}
   703  }
   704  
   705  func TestClose(t *testing.T) {
   706  	t.Run("fail", func(t *testing.T) {
   707  		give := errors.New("great sadness")
   708  		got := Close(newCloserMock(t, give)).Invoke()
   709  		assert.Same(t, give, got)
   710  	})
   711  
   712  	t.Run("success", func(t *testing.T) {
   713  		got := Close(newCloserMock(t, nil)).Invoke()
   714  		assert.Nil(t, got)
   715  	})
   716  }
   717  
   718  func TestAppendIntoNil(t *testing.T) {
   719  	t.Run("nil pointer panics", func(t *testing.T) {
   720  		assert.Panics(t, func() {
   721  			AppendInto(nil, errors.New("foo"))
   722  		})
   723  	})
   724  
   725  	t.Run("nil error is no-op", func(t *testing.T) {
   726  		t.Run("empty left", func(t *testing.T) {
   727  			var err error
   728  			assert.False(t, AppendInto(&err, nil))
   729  			assert.Nil(t, err)
   730  		})
   731  
   732  		t.Run("non-empty left", func(t *testing.T) {
   733  			err := errors.New("foo")
   734  			assert.False(t, AppendInto(&err, nil))
   735  			assert.Equal(t, errors.New("foo"), err)
   736  		})
   737  	})
   738  }
   739  
   740  func TestAppendFunc(t *testing.T) {
   741  	var (
   742  		errDeferred = errors.New("deferred func called")
   743  		errOriginal = errors.New("original error")
   744  	)
   745  
   746  	stopFunc := func() error {
   747  		return errDeferred
   748  	}
   749  
   750  	err := func() (err error) {
   751  		defer AppendFunc(&err, stopFunc)
   752  
   753  		return errOriginal
   754  	}()
   755  	assert.Equal(t, []error{errOriginal, errDeferred}, Errors(err), "both deferred and original error must be returned")
   756  }
   757  
   758  func errorPtr(err error) *error {
   759  	return &err
   760  }
   761  
   762  type closerMock func() error
   763  
   764  func (c closerMock) Close() error {
   765  	return c()
   766  }
   767  
   768  func newCloserMock(tb testing.TB, err error) io.Closer {
   769  	var closed bool
   770  	tb.Cleanup(func() {
   771  		if !closed {
   772  			tb.Error("closerMock wasn't closed before test end")
   773  		}
   774  	})
   775  	return closerMock(func() error {
   776  		closed = true
   777  		return err
   778  	})
   779  }
   780  

View as plain text