Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Are error comparisons supported? #24

Closed
gnahckire opened this issue Jul 25, 2017 · 4 comments
Closed

Are error comparisons supported? #24

gnahckire opened this issue Jul 25, 2017 · 4 comments
Labels

Comments

@gnahckire
Copy link

gnahckire commented Jul 25, 2017

Does this library currently support comparisons of the error type?

I tried playing around with AllowUnexported and cmpopts.IgnoreUnexported but, can't seem to figure out how to make work.

not-working:

package main

import (
	"errors"

	"github.com/google/go-cmp/cmp"
)

func main() {
	err1 := errors.New("err1")
	err2 := errors.New("err2")
	Match(err1, err2)
}

func Match(expected, result interface{}) {
	test := cmp.Diff(expected, result)
	if test != "" {
		panic(test)
	}
}
@dmitshur
Copy link
Member

Since it may be relevant, here's something I've used when I wanted to compare error (strings):

// equalError reports whether errors a and b are considered equal.
// They're equal if both are nil, or both are not nil and a.Error() == b.Error().
func equalError(a, b error) bool {
	return a == nil && b == nil || a != nil && b != nil && a.Error() == b.Error()
}

@gnahckire
Copy link
Author

Thanks! I'm doing that currently. Just double checking to make sure I wasn't missing something.

@dsnet
Copy link
Collaborator

dsnet commented Jul 25, 2017

Hi @gnahckire, it's intentional that this package does not handle errors by default since there's no sensible policy that works for every use case, and it's the responsibility of the user to define the one that makes sense.

Some simple comparers that you could use:

// equateNilError reports errors to be equal only if both are nil.
equateNilError := cmp.Comparer(func(x, y error) bool {
    return x == nil && y == nil
})
// equateAnyError reports errors to be equal only if both are nil or both are non-nil.
equateAnyError := cmp.Comparer(func(x, y error) bool {
    return (x == nil) == (y == nil)
})

What @shurcooL suggests is a possibility, but I want to warn you that the error message provided by most packages is not stable, and something as simple as a grammar change can break your test. If you are simply checking that error messages from your own package match, then this approach is fine.

// equateErrorMessage reports errors to be equal if both are nil
// or both have the same message.
equateErrorMessage := cmp.Comparer(func(x, y error) bool {
    if x == nil || y == nil {
        return x == nil && y == nil
    }
    return x.Error() == y.Error()
})

Even more advanced, if you know properties about the errors you expect, you can write better equate functions:

// sentinelErrors is a set of common sentinel errors
// (i.e., those that are safe to compare directly using the == operator.
sentinelErrors := map[error]bool{io.EOF: true, io.ErrUnexpectedEOF: true, ...}

equateError := cmp.Comparer(func(x, y error) bool {
    if x == nil || y == nil {
        return x == nil && y == nil
    }

    // Check if x and y are sentinel errors.
    if sentinelErrors[x] || sentinelErrors[y] {
        return x == y
    }

    // Compare error based on their properties.
    switch {
    case os.IsExist(x) || os.IsExist(y):
        return os.IsExist(x) && os.IsExist(y)
    case os.IsPathSeparator(x) || os.IsPathSeparator(y):
        return os.IsPathSeparator(x) && os.IsPathSeparator(y)
    case os.IsPermission(x) || os.IsPermission(y):
        return os.IsPermission(x) && os.IsPermission(y)
    }

    // Default to saying that errors are equal
    return true
})

As you can imagine, the set of possible errors in Go is infinite, so it does not make sense for the cmp package to provide the functionality to handle them. It is the users responsibility to know how to properly compare the errors they are interested in.

@dsnet dsnet added question and removed question labels Jul 25, 2017
@gnahckire
Copy link
Author

Awesome. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants