-
Notifications
You must be signed in to change notification settings - Fork 210
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
Convenient FilterPath functions #75
Comments
If you're just trying to ignore a single struct field, does
Iterating over the steps is the correct approach.
Interestingly, when I first built
It's been on my radar for a while to write a tutorial for how to use cmp. The examples today are not great. The tutorial would either be in the form of a GitHub wiki article (with a link from godoc) or in the form of a series of godoc Examples that go from incredibly simple to more complex. I haven't figure out what I'm going to do there. If you think you have a good example to contribute, I'm willing to take a look. |
I would like to avoid having to ignore it. I want to use a Comparer, but only for specific fields (ex: https://github.com/gotestyourself/gotestyourself/pull/62/files#diff-3c98f0bec66867cf0fc114dece491b4aR80). The Comparer will verify that both values are non-zero and within some threshold of each other. If the field were just ignored the test would pass even if the field had a zero value, which is bad if you want the field to have a value of
I think it would be possible to make something that is type safe, ex: dnephin/gotest.tools@002bdd8 Every Edit: I think a similar approach could be done with a set of functions instead of parsing a string. Each step would be matched using something like: pathFilter := Path(IsField("Summary"), IsSlice(), IsField("Elapsed")) which would match a path of I'll try to put together a clean example of |
What I mean by lack of type safety is that: type Foo struct { Hello string }
var x, y Foo
cmp.Equal(x, y, cmp.FilterPath(Spec("Helllo"), cmp.Ignore())) does not detect that "Helllo" is misspelled. That is because |
Ah yes, I see what you mean. I think generally these failures to match would be obvious when the test fails, but probably not easy to debug. Don't you still have that problem with the custom path filters? You still have to compare I created a prototype of the second approach (using functions instead of parsing a string): gotestyourself/gotest.tools@master...dnephin:gocmp-opts-2 With this approach a path would be: path := Path(
Type(node{}),
Step(Field("Ref"), Type(&node{})),
Indirect,
Step(Field("Children"), Type([]node{})),
Slice,
Step(Field("Labels"), Type(map[string]node{})),
MapKey("first"),
Step(Field("Value"), Type(0)),
) It seems to me like this is unnecessarily verbose. If you just checked the type of one step was a reference, slice, or map, then you know the next will need to be With less strict matching logic this could be (the naming needs some work): partial := PathPartial(
Step(Field("Ref"), Type(&node{})),
Step(Field("Children"), Type([]node{})),
Step(Field("Labels"), Type(map[string]node{})),
Step(Field("Value"), Type(0)),
) With this approach a user can check all the types if they want to, or reduce it to just field matching. Does something like this look more viable? |
I looked around go-cmp/cmp/cmpopts/struct_filter.go Lines 15 to 28 in 0c93777
This is much better than having to define the entire path, and should be type safe. What's the status of "concerns of how helper filters can be composed together easily" ? Another option, which I'm also fond of, would be to expose just the filter part: func FieldFilter(typ interface{}, name string) func(p cmp.Path) bool {
sf := newStructFilter(typ, name)
return sf.filter
} I'd like to open a PR that exports one of these. |
I remember now some of my concerns. I wished I had expanded more on them when I wrote that TODO. 1. Returning only pf, vf := cmpopts.HelperFilters(...)
opt := cmp.FilterPath(pf, cmp.FilterValues(vf, ...)) For that reason, I'm not sure a helper that returns a 2. Should |
Thanks for the explanation. My initial reaction is: 1. Returning only func(cmp.Path) bool and func(interface{}) bool doesn't compose well To provide a filter of both path and values why not just expose that as an cmp.Option directly (instead of returning separately filters) ? func HelperFilters(..., opt cmp.Option) cmp.Option {
...
return cmp.FilterPath(pf, cmp.FilterValues(vf, ...))
} This helper can be combined with any other Option. This is already the pattern used by I think that is a good argument against the "alternate option" from my previous comment. 2. Should FilterField(MyStruct{}, "MyField", opts) apply exactly on MyStruct.MyField or the entire sub-tree beneath it? I was thinking about this as well. For the cases where For the case where the field has a slice or map value an exact match is appropriate because trying to match against specified indexes or keys isn't support by So for all the cases where |
I apologize for the delay in replying. Adding |
Fair enough. I've read over and commented on #36 to see if I understand the problem correctly. For now I've implemented PathField: https://github.com/gotestyourself/gotestyourself/pull/86/files#diff-3c98f0bec66867cf0fc114dece491b4aR99 |
Hello, I've been using
go-cmp
for a bit now and I've found that the two most common cases for needing anOption
are:AllowUnexported
/IgnoreUnexported
- which is already easy to useFilterPath
for fields that have values set fromtime.Now()
(time.Time
, ortime.Duration
)It wasn't immediately obvious to me how to create a correct
FilterPath
filter function. After a quick scan of the godoc my first approach was something like this:This worked, but it seems to me like it could potentially match fields that I don't want it to match if I used it with a large nested structure where multiple types had an
Elapsed
field.Digging into the godoc further I noticed that a bunch of the types embedded
PathStep
, which made me realize that a more correct way of usingPath
would probably be to iterate over the steps, type assert to the expected type, and compare toName()
orKey()
.My next iteration (gotestyourself/gotest.tools#62) looks something like this:
Which could be made a lot simpler if it's expected that
PathStep.String()
is stable and wont change between minor versions:The same idea could be applied using
GoString()
if that is considered stable. The idea behind both of these functions is that it's much easier for a reader to understand a dotted path string, than it is to read a long function with a bunch of type assertions.Questions:
PathStep
? Is it similar tofieldPathWithTypes
?PathFilter
filter function from a simple string? This could be something likefieldPathWithTypes
or it could be more verbose (to match against something likeGoString()
. I believe the only type that can't be easily translated from a string representation would beTransform
.FilterPath
(based on feedback from these other questions) ?The text was updated successfully, but these errors were encountered: