Age | Commit message (Collapse) | Author |
|
The previous heuristic of treating strings as binary data
if it contains any invalid UTF-8 was too strict.
Loosen the heuristic to check if most of the characters
are printable text.
Fixes #257
|
|
Avoid diffing by lines if it turns out to be significantly less
efficient than diffing by bytes.
Before this change:
(
"""
- d5c14bdf6bac81c27afc5429500ed750
- 25483503b557c606dad4f144d27ae10b
- 90bdbcdbb6ea7156068e3dcfb7459244
- 978f480a6e3cced51e297fbff9a506b7
+ Xd5c14bdf6bac81c27afc5429500ed750
+ X25483503b557c606dad4f144d27ae10b
+ X90bdbcdbb6ea7156068e3dcfb7459244
+ X978f480a6e3cced51e297fbff9a506b7
"""
)
After this change:
strings.Join({
+ "X",
"d5c14bdf6bac81c27afc5429500ed750\n",
+ "X",
"25483503b557c606dad4f144d27ae10b\n",
+ "X",
"90bdbcdbb6ea7156068e3dcfb7459244\n",
+ "X",
"978f480a6e3cced51e297fbff9a506b7\n",
}, "")
|
|
Even with an optimal diffing algoritm, coalescing adjacent edit groups
may cause the corresponding pair of strings for an edit group to have
leading or trailing spans of equal elements.
While this is technically a correct representation of a diff,
it is a suboptimal outcome. As such, scan through all unequal groups and
move leading/trailing equal spans to the preceding/succeeding equal group.
Before this change:
strings.Join({
"org-4747474747474747,bucket-4242424242424242:m,tag1=a,tag2=aa",
- ",#=_value _value=2 ",
+ " _value=2 ",
`11 org-4747474747474747,bucket-4242424242424242:m,tag1=a,tag2=bb`,
- ",#=_value _value=2 2",
+ " _value=2 2",
`1 org-4747474747474747,bucket-4242424242424242:m,tag1=b,tag2=cc`,
- ",#=_value",
` _value=1 21 org-4747474747474747,bucket-4242424242424242:m,tag1`,
"=a,tag2",
- "=dd,#=_value",
+ "=dd",
` _value=3 31 org-4747474747474747,bucket-4242424242424242:m,tag1`,
- "=c,#=_value",
+ "=c",
` _value=4 41 `,
}, "")
After this change:
strings.Join({
"org-4747474747474747,bucket-4242424242424242:m,tag1=a,tag2=aa",
- ",#=_value",
` _value=2 11 org-4747474747474747,bucket-4242424242424242:m,tag1`,
"=a,tag2=bb",
- ",#=_value",
` _value=2 21 org-4747474747474747,bucket-4242424242424242:m,tag1`,
"=b,tag2=cc",
- ",#=_value",
` _value=1 21 org-4747474747474747,bucket-4242424242424242:m,tag1`,
"=a,tag2=dd",
- ",#=_value",
` _value=3 31 org-4747474747474747,bucket-4242424242424242:m,tag1`,
"=c",
- ",#=_value",
` _value=4 41 `,
}, "")
|
|
s/seperate/separate/
|
|
FormatDiff should only set the verbosity to 3 if the current verbosity
is lower than 3. Otherwise, it may remove an intended higher verbosity
setting causing the reporter output to not differentiate between
two large values that are different at the end.
While we are at it, increase the maxVerbosityPreset to 6.
|
|
Specialized diffing strings and slices should occur for
interface types where both values have the same concrete type.
This is especially relevant for protocmp.Transform,
which transforms every proto.Message as a map[string]interface{}.
|
|
Use the standard definition of errors.Is to implement compareErrors with
≥go1.13. Retain the implementation using golang.org/x/xerrors for versions <go1.13.
This will allow packages using newer Go versions and already relying on
the errors package to get rid of the transitive dependency on
golang.org/x/xerrors.
|
|
Map keys should have a sensible verbosity limit imposed,
otherwise the reporter can end up printing a massive data structure
that cannot reasonably fit in memory.
|
|
A previous attempt to add non-determinism to the diffing algorithm
unfortunately broke the algorithm for half the cases.
This change modifies the algorithm to truly switch between starting
with a forward search versus a reverse search.
The main for-loop of Difference would switch repeatedly between
performing a forward search, then a reverse search, and vice-versa.
Since we can't jump into the middle of a for-loop to start with the
reverse search first, we use a series of labels and goto statements
to accomplish the same effect.
Fixes #238
|
|
The description inaccurately describes the operation of Diff,
which is y - x, where a '+' prefix denotes elements added from y
and a '-' prefix denotes elements removed from x.
For example:
// Consider this call to Diff and its result.
x y
cmp.Diff({b:2, c:3}, {a:1, b:2}) => {+a:1, b:2, -c:3}
// Consider the same in mathematical notation.
y - x
{a:1, b:2} - {b:2, c:3} = {+a:1, b:2, -c:3}
|
|
This reverts commit ab46b8bd0abd4c4557cc4709ad7ae12d47570603.
The upstream change in Go1.16 has been rolled back.
See golang/go#42123
|
|
In Go1.16, the reflect.Type.NumMethod method will no longer report
unexported fields, matching the documented behavior on the method.
This means that t.NumMethod() == 0 is no longer a reliable means
to detect whether an interface type is the empty interface or not.
Fix the code to check whether the empty interface itself implements
the target type.
|
|
Add an example for IgnoreFields. This resuses the test data from the example
in the cmp package to provide consistency between examples.
|
|
There is no LICENSE.md file, but there is a LICENSE file.
|
|
If cmp panics because it is trying to access an unexported field,
specially suggest the use of cmpopts.EquateErrors if the parent type
implements the error interface.
Fixes #233
|
|
Fix the documentation on Diff. It was mentioning the plus or minus sign being printed if the field was added to y or removed from y, but both are the same. Fix it so that it properly mentions the use of a minus sign for elements removed from x.
|
|
For strings, []bytes containing text data, Error method output, and
String method output, use the triple-quoted syntax.
This improves readability by presenting the data more naturally
compared to a single-line quoted string with many escaped characters.
|
|
Adjust the panic message to be more specific about what the user should do, and reduces the need for the user to look at the source code.
|
|
If a panic occurs while calling String or Error,
the reporter recovers from it and ignores it, proceeding with
its usual functionality for formatting a value.
|
|
Previously, the reporter could handle formatting values with cycles
in that it did not crash with a stack overflow. However, the output
was not particularly understandable as it did not surface to the user
why a particular value was truncated, and if it was truncated due
to a cyclic reference, what was the referent.
This change annotates the reporter tree with pointer information
so that a later pass can inject reference information if it is needed
to produce more understandable output.
Consider the following example:
map[string]*cmp_test.CycleAlpha{
"Foo": &⟪ref#0⟫{
Name: "Foo",
Bravos: map[string]*cmp_test.CycleBravo{
"FooBravo": &{
- ID: 101,
+ ID: 0,
Name: "FooBravo",
Mods: 100,
Alphas: {"Foo": &⟪ref#0⟫(...)},
},
},
},
}
This graph contains a cycle. To ensure that a graph can be formatted,
the cycle is truncated as indicated with: &⟪ref#0⟫(...).
The referent was identified earlier with: &⟪ref#0⟫{...}.
|
|
Provide a unique name for every test case.
Provide a reason for every test case.
The purpose of a unique name is so that insertion/removal of a
case does not cause all subsequent names to suddenly shift,
causing a larger number of differences in testdata/diffs.
|
|
The function now handles unexported fields since #203.
|
|
The reporter tries to aggresively elide data that is not interesting
to the user. However, doing so many result in an output that does not
visually indicate the difference between semantically different objects.
This CL modifies the reporter to try increasingly verbose presets until
two different objects are formatted differently.
This CL includes a custom implementation of reflect.Type.String that
can print the type with fully qualified names to disambiguate types
that happen to have the same base package name.
Fixes #194
|
|
A common complaint is that the reporter it prints out too much
irrelevant information, resulting in a low signal-to-noise ratio.
Improve this metric by imposing a verbosity limit. For nodes that
are equal, we set the verbosity level is a lower value than when
the nodes are inequal.
Other minor changes:
* Adjust heuristic for triple-quote usage to operate on more cases.
* Elide type more aggressively for equal nodes.
* Printing the address for a slice includes the length and capacity.
* The pointed-at value for map keys are printed.
|
|
For large slices, arrays, and maps, the reporter can be unreadable
if there are many differences. Limit the number of results to some
reasonable maximum.
|
|
The reporter output is documented as unstable.
The API for custom reporters also specifies that the diffing of
slices is unstable.
Introduce deliberate instability to the diffing algorithm so that
we have the flexibility to improve it in the future.
The current algorithm optimizes for speed, rather than optimality,
so there is much room for improvement.
|
|
Using strings.Join to denote differences in a multi-line string is
visually noisy due to extensive use of quotes and escape sequences.
Add a custom triple-quote syntax that unambiguously shows line
differences with less visual noise.
If the triple-quote syntax cannot unmabiguously show differences,
then the reporter falls back on using the strings.Join format,
which is never ambiguous.
Fixes #195
|
|
|
|
For correctness, cmp checks applicability of the options for every
element in a slice. For large []byte, this is a significant performance
detriment. The workaround is to specify Comparer(bytes.Equal).
However, we would still like to have the batched diffing if the
slices are different. Specialize for this situation.
|
|
Rather than having a single element on each line,
which hurts readability due to the need for scrolling quite a bit.
Batch multiple elements for simple lists to be on a single line.
Fixes #170
|
|
Specify the exact minimumally supported version of Go required
in order to address certain TODOs. This makes it easier to filter
out inactionable TODOs.
|
|
A shallow copy with reflect.ValueOf(v.Interface()) does not work
if v is a nil interface value. Special case the edge case by
checking for a nil value and create a new one use reflect.Zero.
|
|
The current implementation for forcibly exporting fields relies on
the reflect.Value.Addr method for the parent struct value,
where it copies a non-addressable struct onto to the heap so
that it is addressable. However, this action leaks a subtle detail
of how the internal implementation works since the accessed field
for within a struct is only addressable if and only if the
parent struct is also addressable.
Modify the implementation to avoid leaking this implementation detail
by shallow copying the accessed unexported field to remove any
notion of addressability if the parent struct is also unaddressable.
Fixes #181
|
|
The choice to avoid traversing unexported fields by default makes
sense for the semantics of normal comparisons. However, the goal
of the reporter is to prioritize humanly readable output.
As such, it seems appropriate to forcibly export fields.
This allows the String method to be called on such values.
|
|
Diff is most often used in tests where the expected outcome
is equality (and thus no reported difference). Optimize for this
situation by performing a fast-pass equality check and only fall
back on constructing a report if inequal.
|
|
Modify IgnoreFields to permit the specification of unexported fields.
To avoid a bug with reflect.Type.FieldByName, we disallow forwarding
on unexported fields. See https://golang.org/issue/4876 for details.
Fixes #196
|
|
The custom diff output for slices does not accurately reflect the minute
differences between a nil slice and an empty slice.
Avoid the custom diffing if either value is nil.
|
|
In general, decimal formatting is preferred except for []byte
where hexadecimal is preferred for individual elements.
Fixes #185
|
|
Refactor the unit tests to read the diffs from a file
rather than being stored as a Go string literal.
The advantage of using a golden file is to ease
updating the diffs whenever the reporter output is changed.
|
|
|
|
Remove redundant "be".
|
|
This package was never intended to be used in production code.
Document the expectation that this package was only intended
to be used for writing tests.
|
|
Previously, trying to call Equal on a graph would result in a stack-overflow
due to infinite recursion traversing cycles on a graph.
While a vast majority of Go values are trees or acyclic graphs, there exist
a small number of cases where graph equality is required.
As such, we add cycle detection to Equal and define what it means for two graphs
to be equal. Contrary to reflect.DeepEqual, which declares two graphs to be
equal so long any cycle were encountered, we require two graphs to have
equivalent graph structures.
Mathematically speaking, a graph G is a tuple (V, E) consisting of the set of
vertices and edges in that graph. Graphs G1 and G2 are equal if V1 == V2,
E1 == E2, and both have the same root vertex (entry point into the graph).
When traversing G1 and G2, we remember a stack of previously visited edges
ES1 and ES2. If the current edge e1 is in ES1 or e2 is in ES2, then we know that
a cycle exists. The graphs have the same structure when the previously
encountered edge ep1 and ep2 were encountered together.
Note that edges and vertices unreachable from the root vertex are ignored.
Appreciation goes to Eyal Posener (@posener), who proposed a different
(but semantically equivalent) approach in #79, which served as inspiration.
Fixes #74
|
|
The EquateErrors helper equates errors according to errors.Is.
We also declare a sentinel AnyError value that matches any
non-nil error value.
This adds a dependency on golang.org/x/xerrors so that we can
continue to suppport go1.8, which is our current minimally
supported version of Go.
Fixes #89
|
|
Adjust coding style of EquateApproxTime to match EquateApprox.
|
|
Add an Exporter option that accepts a function to determine
which struct types to permit access to unexported fields.
Treat this as a first-class option and implement AllowUnexported
in terms of the new Exporter option.
The new Exporter option:
* Better matches the existing style of top-level options
both by name (e.g., Comparer, Transformer, and Reporter)
and by API style (all accept a function).
* Is more flexible as it enables users to functionally
implement AllowAllUnexported by simply doing:
Exporter(func(reflect.Type) bool { return true })
Fixes #40
|
|
In the panic message when accessing an unexported field,
print the full name of the type for user convenience.
|
|
Fixes #167
|
|
|
|
* Fix IsZero to properly report false for IsZero(-0.0) since
we define IsZero as whether it is equal to the zero memory value.
* Add note to isLess that we don't need to handle -0.0 since
we can't possibly have both keys present in the same map.
* Use sort.SliceStable in SortedKeys for deterministic output since
it is possible to have both -0.0 and +0.0 from two different maps.
The zero key from the left left map will be taken over the right map.
|