-
Notifications
You must be signed in to change notification settings - Fork 10.9k
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
Contract of Collection.contains violated in AbstractBaseGraph.edges #5843
Comments
The @Test
public void network_asGraph_similar_issue() {
MutableNetwork<Integer, String> network = NetworkBuilder.undirected()
.build();
network.addEdge(1, 2, "1-2");
Graph<Integer> graph = network.asGraph();
Set<EndpointPair<Integer>> edges = graph.edges();
EndpointPair<Integer> ordered = EndpointPair.ordered(1, 2);
Assertions.assertTrue(edges.contains(ordered));
Assertions.assertTrue(edges.stream().anyMatch(ordered::equals)); // fails
} |
I've investigated this a bit out of interest, and I think the problem is that guava/guava/src/com/google/common/graph/AbstractBaseGraph.java Lines 86 to 96 in 448e326
Specifically, it uses a custom method: guava/guava/src/com/google/common/graph/AbstractBaseGraph.java Lines 180 to 182 in 448e326
By comparison, here's the javadoc for guava/guava/src/com/google/common/graph/EndpointPair.java Lines 131 to 137 in 448e326
|
For clarity, I've investigated |
I suppose the next question is: is this difference to the I can see an advantage to the current approach. It allows all these assertions to pass: @Test
public void test1() {
MutableGraph<Integer> undirectedGraph = GraphBuilder.undirected().build();
undirectedGraph.putEdge(1, 2);
assertThat(undirectedGraph.edges().contains(EndpointPair.ordered(1, 2))).isTrue(); // passes
assertThat(undirectedGraph.edges().contains(EndpointPair.ordered(2, 1))).isTrue(); // passes
assertThat(undirectedGraph.edges().contains(EndpointPair.unordered(1, 2))).isTrue(); // passes
assertThat(undirectedGraph.edges().contains(EndpointPair.unordered(2, 1))).isTrue(); // passes
} By comparison, using @Test
public void test2() {
MutableGraph<Integer> undirectedGraph = GraphBuilder.undirected().build();
undirectedGraph.putEdge(1, 2);
EndpointPair<Integer> undirectedEdge = undirectedGraph.edges().iterator().next();
assertThat(undirectedEdge.equals(EndpointPair.ordered(1, 2))).isTrue(); // fails
assertThat(undirectedEdge.equals(EndpointPair.ordered(2, 1))).isTrue(); // fails
assertThat(undirectedEdge.equals(EndpointPair.unordered(1, 2))).isTrue(); // passes
assertThat(undirectedEdge.equals(EndpointPair.unordered(2, 1))).isTrue(); // passes
} @jrtom Do you have any thoughts on this? I hope you don't mind me including you in this discussion. |
I haven't been involved in this, but it was interesting enough that I skimmed through the history. It looks like this used to be "correct" but was pretty explicitly "broken" by a subsequent change. The change was definitely intended to make different I would default to saying that this is "wrong," but there have been so many tricky tradeoffs involved in the design of |
I'm able to change |
There's an interesting method in /**
* In some cases our graph implementations return custom sets that define their own size() and
* contains(). Verify that these sets are consistent with the elements of their iterator.
*/
static <T> Set<T> sanityCheckSet(Set<T> set) {
assertThat(set).hasSize(Iterators.size(set.iterator()));
for (Object element : set) {
assertThat(set).contains(element);
}
assertThat(set).doesNotContain(new Object());
assertThat(set).isEqualTo(Util.setOf(set));
return set;
} |
@h908714124 thanks for the catch. This is a bug. It arose from a bug fix, but @cpovirk is correct that the behavior of @jbduncan thanks for the analysis; you are correct about the cause. The original review was about fixing undefined behavior for mutations involving the case where someone added an unordered Unfortunately, I think that I got a bit too cute with the solution (and I don't think any of the reviewers noticed the problem). Technically adding an ordered edge As @h908714124 pointed out, we should not have defined @cpovirk, since you've apparently already identified the failing tests (which implies that you have a CL with this change), I'd be happy to review a fix for this. Otherwise I'll try to get to it. |
Sadly, I have a CL that blindly changes how |
You're right that we can't simply change how This method is also used for @Override
public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
checkNotNull(endpoints);
if (!isOrderingCompatible(endpoints)) {
return false;
}
N nodeU = endpoints.nodeU();
N nodeV = endpoints.nodeV();
return nodes().contains(nodeU) && successors(nodeU).contains(nodeV);
} whose contract specifies: /**
* Returns true if there is an edge that directly connects {@code endpoints} (in the order, if
* any, specified by {@code endpoints}). This is equivalent to {@code
* edges().contains(endpoints)}. This implies that the semantic change needed for At that point, I'm not 100% sure whether it makes sense to continue special-casing mutations in that way. Would be interested in hearing other opinions on this. |
Do Google's internal search tools allow queries like the following?
If so, I think this would allow the potential disruption of any change to be measured. Otherwise, my gut feeling is for |
Thanks, @jbduncan.
As far as I can tell, neither of these cases exist in Google's code base. However, that doesn't tell us whether they exist in the rest of the world. :)
I think we've settled on this change: that was the original subject of this issue, and I agree that we need to not violate the
This is the remaining question, yes. :) |
Fix applied above per #6058. The practical results:
As a result, all ordering/directionality mismatches are handled identically, rather than allowing undirected graphs to be more permissive than directed graphs. |
Let
p
be anEndpointPair<Integer>
. The method AbstractBaseGraph#edges can return aSet<EndpointPair<Integer>> edges
for whichedges.contains(p)
istrue
, bute.equals(p)
isfalse
for each elemente
. This violates the contract of java.util.Collection#contains:Note the failing assertion in the following test:
The text was updated successfully, but these errors were encountered: