Comments (18)
Oh, that's good to know, thanks! (I'm using Truth 0.23).
As far as dealing with the original issue is concerned, I think a sensible
scheme for disambiguation could be:
assertThat(ImmutableList.of("1")).containsExactly(1L);
java.lang.AssertionError: Not true that <[1]> contains exactly <[1]>.
It is missing <[1 (java.lang.Long)]>
This could be done by introducing a Disambiguator class that keeps track of
the string representations of all the elements. When time comes to emit the
differences, the disambiguator is asked to disambiguate each element before
it is emitted.
from truth.
What if the hashcodes are equal too? !a.equals(b)
does not imply a.hashCode() != b.hashCode()
. :)
I would be happy if the basic case of int literals vs longs (and probably float literals vs doubles) worked for collections.
from truth.
We do disambiguate for direct comparisons, but we don't do it for collections.
It's a little difficult to come up with a perfect error message here though. If we do what you're proposing, these two would have identical error messages:
assertThat(ImmutableList.of("1L")).containsExactly(1);
assertThat(ImmutableList.of(1L)).containsExactly(1);
from truth.
I generally agree with pulling out strings to look like the literals they
started as. This means re-escaping double-quotes, but it should be
unambiguous (assuming you also single quote chars). This embeds string- or
char-ness nicely in the output.
Obviously this is hard when things like \uxxxx chars are used (there's no
way to know what went into the string).
My approach to this would be simply construct the common case error message
parts and if both the expected and actual strings were equal, annotate then
with type information.
Not true that <1> (java.lang.Integer) is equal to <1> (java.math.BigInteger)
Obviously it's harder for collections, but maybe just where types differ:
Not true that <[1, 2 (java.lang.Integer), 3]> is equal to <[1, 2 (
java.math.BigInteger), 3]>
Even then there's an issue with things like Unicode combining characters:
http://en.wikipedia.org/wiki/Combining_character
Which could lead to messages like:
Not true that <"ångström"> equals <"ångström">
In this case you'd need to use a machine form of some kind (*):
Not true that <"ångström"> (\u00E5ngstr\u00F6m) equals <"ångström">
(\u0061\u030Angstr\u00F6m)
I feel that Truth is great opportunity to "do the right thing" for every
use case; even if it's super hard to get it right inside Truth.
David
(*) This reminds me to look at adding Unicode aware assertions into a truth
extension for use with I18n code.
On 11 November 2014 15:45, James Ring [email protected] wrote:
In your example, wouldn't the error messages look like
Not true that <["1L"]> contains exactly <[1L]>
?
I suppose it's possible to construct examples that would foil any
disambiguation scheme. I just thought the int vs. long thing is fairly
common.—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
The general problem is that a.equals(b)
does not imply a.toString().equals(b.toString())
. It will always be possible for unequal objects to have equal toString(), if they are of the same type then adding type information will obviously not help. I don't think there is a general solution to that problem.
However, emitting the type when toString() is equal and the types differ would definitely help for the case of integer literals, which I think is a very common use case.
from truth.
Oh, and if the objects are not equals, but the toString() is the same, and
the type is the same, then you print the hashcode (and if that's the same
you print the Object hashcode).
By this time I'd expect a good few lines of "Warning: Stop being daft..."
type messages with it too.
I know it sounds like a lot of work, but every time I get a useless assert
failure message, it makes me sad. Truth would really differentiate itself
in my eyes by going the extra mile here.
David
On 12 November 2014 08:00, James Ring [email protected] wrote:
The general problem is that a.equals(b) does not imply
a.toString().equals(b.toString()). It will always be possible for unequal
objects to have equal toString(), if they are of the same type then adding
type information will obviously not help. I don't think there is a general
solution to that problem.However, emitting the type when toString() is equal and the types differ
would definitely help for the case of integer literals, which I think is a
very common use case.—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
Worth mentioning: a long in int range will have the same hashCode as that
int, so you're not buying yourself anything for that case...
On Wed Nov 12 2014 at 11:03:11 AM James Ring [email protected]
wrote:
What if the hashcodes are equal too? !a.equals(b) does not imply a.hashCode()
!= b.hashCode(). :)I would be happy if the basic case of int literals vs longs (and probably
float literals vs doubles) worked for collections.—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
Yes, but the types are distinct, so we'd already have found uniqueness.
x.toString() > x.getClass() > x.hashcode() > Objects.hashcode(x)
Stop whenever we see a difference. There is always going to be something
we can put in front of a user to distinguish the cases.
David
On 12 November 2014 12:19, Louis Wasserman [email protected] wrote:
Worth mentioning: a long in int range will have the same hashCode as that
int, so you're not buying yourself anything for that case...On Wed Nov 12 2014 at 11:03:11 AM James Ring [email protected]
wrote:What if the hashcodes are equal too? !a.equals(b) does not imply
a.hashCode()
!= b.hashCode(). :)I would be happy if the basic case of int literals vs longs (and
probably
float literals vs doubles) worked for collections.—
Reply to this email directly or view it on GitHub
#151 (comment).—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
Not always:
class Test {
@Override public int hashCode() { return 0; }
@Override public boolean equals(Object o) { return false; }
@Override public String toString() { return "Silly class"; }
}
It's a pathological case but it demonstrates that there are situations where the objects simply cannot be distinguished. This is because the equals predicate can be defined in terms of things outside of string representation and type.
from truth.
Sorry, that last one should have read:
https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object)
David
On 12 November 2014 18:28, James Ring [email protected] wrote:
Not always:
class Test {
@OverRide public int hashCode() { return 0; }
@OverRide public boolean equals(Object o) { return false; }
@OverRide public String toString() { return "Silly class"; }
}It's a pathological case but it demonstrates that there are situations
where the objects simply cannot be distinguished. This is because the
equals predicate can be defined in terms of things outside of string
representation and type.—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
Flogging the dead horse: this would give something like
Test t = new Test();
assertThat(t).isEqualTo(t);
Expected "Silly class" (identity hashcode=1234) but was "Silly class" (identity hashcode=1234).
There's no way to solve this for all cases.
from truth.
You are totally missing the point here I'm afraid. We only care about
putting out the message when they are different in some way. Even if
everything about their toString/hashcode is the same, to be different in
the assert they must be different instances. Different instances always
have different IDENTITY hash codes. If I literally do what you wrote, the
asset won't fail and therefore we don't care about making failure messages.
David
On 12 Nov 2014 18:38, "James Ring" [email protected] wrote:
Flogging the dead horse: this would give something like
Test t = new Test();
assertThat(t).isEqualTo(t);Expected "Silly class" (hashcode=0) but was "Silly class" (hashcode=0).
There's no way to solve this for all cases.
—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
If you look at the implementation of isEqualTo
, you'll find that identity equality is never considered. In my example, the identity hashcodes are equal and yet the assertion fails, so you'll still end up with a useless error message.
from truth.
Ok, so I would have assumed that identity equality should be considered a
"gospel" anywhere.
Your case is possible but relies on violating identity equality, which is
not a case I particularly care about; it's not realistic and could be
caught with an equality behaviour test (which can be often be automated).
Truth could still catch this by saying:
if (expected == actual) {
assertTrue(Objects.equal(expected, actual), "message about how you
instances are badly implemented");
}
// ... run sane test from here on.
Basically if a human can look at the situation and figure out that, despite
looking the same, the objects didn't match the equality test, Truth can
also do sufficient reasoning and print out messages that are distinct.
There is no case where we have to fall back to "expected but was
" ... none (even if it involves a completely different message in
extreme cases).
David
On 12 November 2014 20:55, James Ring [email protected] wrote:
If you look at the implementation of isEqualTo, you'll find that identity
equality is never considered. In my example, the identity hashcodes are
equal and yet the assertion fails, so you'll still end up with a useless
error message.Try it! :)
—
Reply to this email directly or view it on GitHub
#151 (comment).
from truth.
@sjamesr wrote:
If you look at the implementation of isEqualTo, you'll find that identity equality is never considered. In my example, the identity hashcodes are equal and yet the assertion fails, so you'll still end up with a useless error message.
Hmm, Subject.isEqualTo()
calls Objects.equal()
which checks a == b
.
from truth.
This is easily falsifiable:
@Test
public void sillyTest() {
Object o = new Object() {
@Override
public boolean equals(Object ignored) {
return false;
}
};
assertThat(o).isEqualTo(o);
}
from truth.
@sjamesr That test passes for me. I suspect you're using an older version of Truth. I made the change to use Objects.equal()
in Subject.isEqualTo()
back in early August:
339e62d
from truth.
I believe that we now do this. (Certainly we do a bunch of things like this; if we're missing something specific, please let us know.)
from truth.
Related Issues (20)
- Consider reducing ImmutableList array copying HOT 1
- Wanted: Truth8Junit to support Java 8 types with assume() HOT 3
- Compatibility with gradle 7.6 HOT 2
- UsingCorrespondence should fail on use of equals(), like Subject does
- 1.1.4, Guava update leads to java.lang.UnsupportedOperationException at WindowsSecurityDescriptor.java:358 HOT 2
- Add StringSubject isNotBlank or hasText HOT 2
- Idk
- com.google.guava:guava library version update HOT 2
- Matching on exact class type, not a subtype HOT 2
- Write a comparison to Kluent, AssertK, Strikt, Atrium, Kotest, other Kotlin assertion libraries? HOT 13
- Protobuf: allow unpopulated required fields when `comparingExpectedFieldsOnly` HOT 5
- Truth 1.1+ depends on Java 8 bytecode HOT 3
- FAQ entry: Why we don't support multiple chained calls to assertion methods on the same Subject HOT 3
- Port a core set of unit tests to kotlin HOT 2
- JUnit5 support for the existing soft-assertions support (Expect.create JUnit4 Rule) HOT 5
- Document when arrays are compared by their contents vs. their object identity HOT 3
- New extension for Gradle's TestKit HOT 1
- Asymmetric matchers like Jest's expect.stringContaining("") ? HOT 3
- Add MapSubject#containsAnyOf HOT 2
- Allow logical operations on assertions HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from truth.