Skip to content

Commit

Permalink
AppendInvoke: Merge test, real example (#48)
Browse files Browse the repository at this point in the history
Follow up to #47 with the following changes.

- The tests for AppendInvoke with Close duplicate the tests for
  AppendInvoke. These can be merged, and the unit tests for Close only
  need to verify whether the Invoker returned by multierr.Close calls
  the Close function in the provided io.Closer.
- Replace AppendInvoke example with something more realistic.
- Update changelog.
  • Loading branch information
abhinav authored May 6, 2021
1 parent a402392 commit 1014a7c
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 71 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Releases
========

v1.7.0 (unreleased)
===================

- Add `AppendInvoke` to append into errors from `defer` blocks.


v1.6.0 (2020-09-14)
===================

Expand Down
74 changes: 74 additions & 0 deletions appendinvoke_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package multierr_test

import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"

"go.uber.org/multierr"
)

func ExampleAppendInvoke() {
if err := run(); err != nil {
log.Fatal(err)
}
}

func run() (err error) {
dir, err := ioutil.TempDir("", "multierr")
// We create a temporary directory and defer its deletion when this
// function returns.
//
// If we failed to delete the temporary directory, we append its
// failure into the returned value with multierr.AppendInvoke.
//
// This uses a custom invoker that we implement below.
defer multierr.AppendInvoke(&err, RemoveAll(dir))

path := filepath.Join(dir, "example.txt")
f, err := os.Create(path)
if err != nil {
return err
}
// Similarly, we defer closing the open file when the function returns,
// and appends its failure, if any, into the returned error.
//
// This uses the multierr.Close invoker included in multierr.
defer multierr.AppendInvoke(&err, multierr.Close(f))

if _, err := fmt.Fprintln(f, "hello"); err != nil {
return err
}

return nil
}

// RemoveAll is a multierr.Invoker that deletes the provided directory and all
// of its contents.
type RemoveAll string

func (r RemoveAll) Invoke() error {
return os.RemoveAll(string(r))
}
60 changes: 24 additions & 36 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,60 +596,49 @@ func TestAppendInvoke(t *testing.T) {
errors.New("baz"),
),
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
AppendInvoke(tt.into, tt.give)
assert.Equal(t, tt.want, *tt.into)
})
}
}

func TestAppendInvokeClose(t *testing.T) {
tests := []struct {
desc string
into *error
give error // error returned by Close()
want error
}{
{
desc: "append close nil into empty",
desc: "close/empty",
into: new(error),
give: nil,
want: nil,
give: Close(newCloserMock(t, errors.New("foo"))),
want: errors.New("foo"),
},
{
desc: "append close into non-empty, non-multierr",
into: errorPtr(errors.New("foo")),
give: errors.New("bar"),
want: Combine(
errors.New("foo"),
errors.New("bar"),
),
desc: "close/no fail",
into: new(error),
give: Close(newCloserMock(t, nil)),
want: nil,
},
{
desc: "append close into non-empty multierr",
into: errorPtr(Combine(
errors.New("foo"),
errors.New("bar"),
)),
give: errors.New("baz"),
desc: "close/non-empty",
into: errorPtr(errors.New("foo")),
give: Close(newCloserMock(t, errors.New("bar"))),
want: Combine(
errors.New("foo"),
errors.New("bar"),
errors.New("baz"),
),
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
closer := newCloserMock(t, tt.give)
AppendInvoke(tt.into, Close(closer))
AppendInvoke(tt.into, tt.give)
assert.Equal(t, tt.want, *tt.into)
})
}
}

func TestClose(t *testing.T) {
t.Run("fail", func(t *testing.T) {
give := errors.New("great sadness")
got := Close(newCloserMock(t, give)).Invoke()
assert.Same(t, give, got)
})

t.Run("success", func(t *testing.T) {
got := Close(newCloserMock(t, nil)).Invoke()
assert.Nil(t, got)
})
}

func TestAppendIntoNil(t *testing.T) {
t.Run("nil pointer panics", func(t *testing.T) {
assert.Panics(t, func() {
Expand Down Expand Up @@ -689,7 +678,6 @@ func newCloserMock(tb testing.TB, err error) io.Closer {
tb.Error("closerMock wasn't closed before test end")
}
})

return closerMock(func() error {
closed = true
return err
Expand Down
32 changes: 0 additions & 32 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,38 +94,6 @@ func ExampleAppendInto() {
// foo; baz
}

func ExampleAppendInvoke() {
var err error

var errFunc1 multierr.Invoker = multierr.Invoke(func() error {
fmt.Println("call 1 failed")
return errors.New("foo")
})

var errFunc2 multierr.Invoker = multierr.Invoke(func() error {
fmt.Println("call 2 did not fail")
return nil
})

var errFunc3 multierr.Invoker = multierr.Invoke(func() error {
fmt.Println("call 3 failed")
return errors.New("baz")
})

defer func() {
fmt.Println(err)
}()
defer multierr.AppendInvoke(&err, errFunc3)
defer multierr.AppendInvoke(&err, errFunc2)
defer multierr.AppendInvoke(&err, errFunc1)

// Output:
// call 1 failed
// call 2 did not fail
// call 3 failed
// foo; baz
}

type fakeCloser func() error

func (f fakeCloser) Close() error {
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module go.uber.org/multierr
go 1.14

require (
github.com/stretchr/testify v1.3.0
github.com/stretchr/testify v1.7.0
go.uber.org/atomic v1.7.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 1014a7c

Please sign in to comment.