Skip to content

🌱 Add composable matchers to internal/testtypes #5219

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

Conversation

killianmuldoon
Copy link
Contributor

@killianmuldoon killianmuldoon commented Sep 7, 2021

This commit adds composable matching objects to the internal
testtypes package. These types can be used for advanced comparison
between objects in the Cluster API unit and integration tests.

Fixes #5157

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Sep 7, 2021
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
To complete the pull request process, please assign cecilerobertmichon after the PR has been reviewed.
You can assign the PR to them by writing /assign @cecilerobertmichon in a comment when ready.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Sep 7, 2021
@killianmuldoon killianmuldoon force-pushed the pr-integration-test branch 4 times, most recently from dab9113 to 4371995 Compare September 7, 2021 22:12
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 15, 2021
@killianmuldoon killianmuldoon force-pushed the pr-integration-test branch 2 times, most recently from 3669a32 to 0fc889e Compare September 15, 2021 21:39
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 15, 2021
@killianmuldoon
Copy link
Contributor Author

@fabriziopandini this is now reworked and ready for review.

This commit adds composable matching objects to the internal
testtypes package. These types can be used for advanced comparison
between objects in the Cluster API unit and integration tests.
A simple integration test is included to show the basic working of
the matchers.
@k8s-ci-robot
Copy link
Contributor

@killianmuldoon: PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 16, 2021
@killianmuldoon killianmuldoon changed the title 🌱 WIP: Add composable matchers to internal/testtypes 🌱 Add composable matchers to internal/testtypes Sep 16, 2021
cmp.Diff(tt.want.MachineDeployments, got.MachineDeployments, ignoreResourceVersion))
// Use EqualObject where an object is created and passed through the fake client. Elsewhere the Equal methoc
// is enough to establish inequality.
g.Expect(tt.want.ClusterClass).To(EqualObject(got.ClusterClass, IgnoreAutogeneratedMetadata))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
g.Expect(tt.want.ClusterClass).To(EqualObject(got.ClusterClass, IgnoreAutogeneratedMetadata))
g.Expect(tt.want.ClusterClass).To(EqualObject(got.ClusterClass, IgnoreAutogeneratedMetadata))

I like this! super clean

)

// This code is a modified version of the mergePatch code from the controllers/topology/internal/mergepatch pkg.
// TODO: Create a shared patchdiff library that can be shared for both patching and matching.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue for this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want a separate package for that? Sometimes a little bit copying is better then a new package/dependency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copying is definitely the right way for now - but having this code in multiple places doesn't seem like the right solution in the long term as its usage might grow - especially if mergepatch goes out of internal. I think having an issue for this is a good idea, and we can decide down the line if we want to actually execute on it.


// NegatedFailureMessage returns a string comparing the full objects after an unexpected match has occurred.
func (m *Matcher) NegatedFailureMessage(other interface{}) (message string) {
return format.Message(other, "not to match", m.original)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's return diff also here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shouldn't be a diff here though right? This will print only when the objects are expected not to match but do so unexpectedly. I can add the diff for now - it will just be an empty string if there's nothing in there.

@fabriziopandini
Copy link
Member

lgtm apart two nits and rebase

Copy link
Member

@sbueringer sbueringer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks very nice!

@@ -21,9 +21,8 @@ import (

"github.com/pkg/errors"
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
"sigs.k8s.io/cluster-api/internal/testtypes"
. "sigs.k8s.io/cluster-api/internal/testtypes"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of dot imports to be honest :/ (because of the "magic-factor")
I understand why we want it for matchers, I'm not sure if we should also use it for our builder funcs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - it's a compromise here from having one package with the two different functions. We are using . imports across our testing for Gomega. Do you have strong feelings about this? I can definitely see both sides.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move it to the new PR

)

// This code is a modified version of the mergePatch code from the controllers/topology/internal/mergepatch pkg.
// TODO: Create a shared patchdiff library that can be shared for both patching and matching.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want a separate package for that? Sometimes a little bit copying is better then a new package/dependency

// These package variables hold pre-created commonly used options that can be used to reduce the manual work involved in
// identifying the paths that need to be compared for testing equality between objects.
var (

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

nit

{"metadata", "managedFields"},
{"metadata", "deletionGracePeriodSeconds"},
{"metadata", "deletionTimestamp"},
{"metadata", "ownerReferences"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we currently need to ignore ownerReferences? If not, it might be good to remove them from the list. When they are set, they are set intentionally by our code. (afaik)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed and you're right that we do set them. I'll remove it.

options *MatchOptions
}

// EqualObject applies the MatchOptions and returns a Matcher for the passed Kubernetes runtime.Object. This function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I would include applies the MatchOptions it almost sounds like we apply them to the object. I probably would just omit it.

}
}

// Match compares the object it's based on to the passed object and returns true if the objects are the same according to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Match compares the object it's based on to the passed object and returns true if the objects are the same according to
// Match compares the current object to the passed object and returns true if the objects are the same according to

nit

return true, nil
}
if otherIsNil || originalIsNil {
return false, fmt.Errorf("can not compare an object with a nil. original :%v , other %v", m.original, other)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return false, fmt.Errorf("can not compare an object with a nil. original :%v , other %v", m.original, other)
return false, fmt.Errorf("can not compare an object with a nil. original %v , other %v", m.original, other)

nit (I assume we want : either for both or for none of the objects)


// Match compares the object it's based on to the passed object and returns true if the objects are the same according to
// the Matcher MatchOptions.
func (m *Matcher) Match(other interface{}) (success bool, err error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (m *Matcher) Match(other interface{}) (success bool, err error) {
func (m *Matcher) Match(actual interface{}) (success bool, err error) {

I looked at a gomega matcher and this seems to be called actual there. I think it would be nice to do the same in our code. (same for other funcs)

return format.Message(other, "not to match", m.original)
}

// calculateDiff applies the MatchOptions and identified the diff between the Matcher object and the passed object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// calculateDiff applies the MatchOptions and identified the diff between the Matcher object and the passed object.
// calculateDiff applies the MatchOptions and identifies the diff between the Matcher object and the passed object.

nit, (or maybe calculates)

}

// filterDiff limits the diff to allowPaths if given and excludes ignorePaths if given. It returns the altered diff.
func filterDiff(diff []byte, allowedPaths, ignorePaths [][]string) ([]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func filterDiff(diff []byte, allowedPaths, ignorePaths [][]string) ([]byte, error) {
func filterDiff(diff []byte, allowPaths, ignorePaths [][]string) ([]byte, error) {

nit (to keep it consistent with the field name)

@JoelSpeed
Copy link
Contributor

Found a typo while having a read through, otherwise looks great

@killianmuldoon killianmuldoon deleted the pr-integration-test branch September 17, 2021 14:33
@killianmuldoon killianmuldoon restored the pr-integration-test branch September 17, 2021 14:34
@killianmuldoon
Copy link
Contributor Author

@JoelSpeed did you mark the typo in your review? I can't seem to see it

cmp.Diff(tt.want.ControlPlane, got.ControlPlane, ignoreResourceVersion))
g.Expect(cmp.Diff(tt.want.MachineDeployments, got.MachineDeployments, ignoreResourceVersion)).To(Equal(""),
cmp.Diff(tt.want.MachineDeployments, got.MachineDeployments, ignoreResourceVersion))
// Use EqualObject where an object is created and passed through the fake client. Elsewhere the Equal methoc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Use EqualObject where an object is created and passed through the fake client. Elsewhere the Equal methoc
// Use EqualObject where an object is created and passed through the fake client. Elsewhere the Equal method

@JoelSpeed
Copy link
Contributor

@JoelSpeed did you mark the typo in your review? I can't seem to see it

I can't press the submit button very well clearly 😓

@killianmuldoon
Copy link
Contributor Author

I can't press the submit button very well clearly 😓

Happens to us all!

@killianmuldoon
Copy link
Contributor Author

killianmuldoon commented Sep 17, 2021

Note: This PR has been closed in favor of #5259 which has an updated, more appropriate, branch name given the changes in this PR.

@sbueringer
Copy link
Member

sbueringer commented Sep 20, 2021

@killianmuldoon Are you referring to the "title" (aka 🌱 Add composable matchers to internal/testtypes)? (just confused because it seems to be the same).

If yes, you can just edit it.

EDIT: Okay I saw in your other PR that you're referring to the branch name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create custom matchers for internal/testtypes
5 participants