-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Structurally compare js objects rather than YAML #404
Conversation
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here with What to do if you already signed the CLAIndividual signers
Corporate signers
ℹ️ Googlers: Go here for more info. |
👍 (I've worked on this with @Bj0rnen) |
@googlebot I signed it! |
@mengqiy on Monday we will restart working on this and we will provide some more information. You mean an example of how it looks with this change and how it was before? About the typing problem, either we use |
Hi @mengqiy! I have added a before vs after to the original comment. I think everything should be in order now. Does it look good from your end? |
indent: 2, | ||
noArrayIndent: true, | ||
skipInvalid: true, | ||
sortKeys: true, | ||
}); | ||
return safeLoad(yaml); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you want to safeDump
and then safeLoad
?
Can we simply compare the ResourceList
returned by configs.toResourceList()
? Are there any downsides?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that it's a roundabout way to implement it. But in practice, returning configs.toResourceList()
causes many test failures for us. Mainly these two types of inequalities:
Expected $.items[0].metadata not to have properties
namespace: undefined
and
Expected $.items[0] to be a kind of IAMPolicyResource, but was Object({ apiVersion: 'iam.cnrm.cloud.google.com/v1alpha2', kind: 'IAMPolicy', metadata: Object({ name: 'iam-policy', namespace: 'project-level' }), spec: Object({ resourceRef: Object({ apiVersion: 'test', kind: 'Test' }), bindings: [ ] }) }).
The first kind of inequality is easy to understand. safeDump
elides all undefined
properties.
The second kind of inequality results from the call to deepCopy
on line 102
const configs = input.deepCopy(); |
I haven't dug into the implementation of
Configs.deepCopy
. But basically, before the equality comparison happens, the input
object goes through a deepCopy
and the expectedOutput
object doesn't. That deepCopy
is apparently not "equality-safe", however whatever differences it creates disappear again upon safeDump
.
We have already relied on the semantics of post-yaml comparison in our test cases. 44/118 of our test cases break if we directly return configs.toResourceList()
from valueOf
. safeDump
returns a string. And if strings s
and t
are equal (and valid YAML), then safeLoad(s)
and safeLoad(t)
must also be equal (safeDump
is deterministic, surely). The proposed change can't break existing tests.
Alternatives
Patch the differences
I suppose it's possible to address the two types of equality differences that we have observed above. We can do a deepCopy
of the o
object that goes into the comparison. We can explicitly filter out undefined
properties with some recursive function. Or maybe modify our kpt functions to be meticulous about this, so that all the undefined
s match up.
But how can we know that these two equality differences are the only ones that could appear? Someone else might depend on other aspects of safeDump
in their tests. It seems a bit arbitrary to patch over these two things specifically.
Rich YAML comparison
Maybe the expect(...).toEqual(...)
can be replaced with some YAML-specific equality comparison function, which takes two YAMLs and outputs a rich diff on the strings? For example a line-based diff. Then we can go back to only using safeDump
and not safeLoad
. That should probably also output readable diffs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanation.
Yep. All good from me 👍 |
@mengqiy is this going to be released automatically? |
No. It needs some manual work to release it. |
No I wouldn't say that we are blocked, but I guess would be good to understand if the new version is days/weeks/months ahead :) Thanks! |
You can expect it in the next 7 days. |
In case you missed it, kpt-functions@0.15.1 has been released. |
We are using the TestRunner to test our own kpt functions. For our use-cases, we have found the output of failed test cases to be insufficient in helping us locate the actual problems. That is because the test assertion compares two rendered YAMLs. These YAML strings are simply too long and the testing library (jasmine) truncates the strings, typically before the actual difference has appeared.
With this change, we compare JavaScript objects instead, and the assertion errors show a vastly improved structural diff between the configs. Now, we have been careful here to not change the behavior in terms of when tests succeed or fail. We've done that by still converting the configs to YAML and then converting them back from YAML. For us, this has been working exactly as intended.
@emmmile
Before vs after
Both before and after the change, diffs are printed like
Expected <foo> to equal <bar>.
. Keep an eye out for theto equal
part in the before diff below:And here's the after diff for the same test case:
The before diff has many issues:
...
. In this test case (and a majority of cases for us), the differences weren't even in the Kubernetes objects part of theConfigs
objects; they were in the results part. So the displayed, truncated, YAML strings are, in fact, 100% equal. The before diff gives us zero information about differences between "expected" and "actual".Configs
objects are rendered as YAML, Kubernetes objects and results included, the diffing only reports that there is a single difference. In the after diff, we see two differences.From the after diff, we can conclude that the two results
result[0]
andresult[1]
were simply placed in the wrong order in theresults
array.