diff --git a/src/main/java/io/github/danthe1st/arebac/data/commongraph/attributed/AttributeValue.java b/src/main/java/io/github/danthe1st/arebac/data/commongraph/attributed/AttributeValue.java index 5ae52b4..2188a72 100644 --- a/src/main/java/io/github/danthe1st/arebac/data/commongraph/attributed/AttributeValue.java +++ b/src/main/java/io/github/danthe1st/arebac/data/commongraph/attributed/AttributeValue.java @@ -60,4 +60,8 @@ static IntAttribute attribute(int value) { static StringAttribute attribute(String value) { return new StringAttribute(value); } + + static BooleanAttribute attribute(boolean value) { + return new BooleanAttribute(value); + } } diff --git a/src/test/java/io/github/danthe1st/arebac/tests/gpeval/FriendOfFriendEvaluateTest.java b/src/test/java/io/github/danthe1st/arebac/tests/gpeval/FriendOfFriendEvaluateTest.java index 0f3cfd9..2b297a5 100644 --- a/src/test/java/io/github/danthe1st/arebac/tests/gpeval/FriendOfFriendEvaluateTest.java +++ b/src/test/java/io/github/danthe1st/arebac/tests/gpeval/FriendOfFriendEvaluateTest.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import io.github.danthe1st.arebac.data.graph_pattern.GPEdge; import io.github.danthe1st.arebac.data.graph_pattern.GPGraph; @@ -21,10 +22,11 @@ import io.github.danthe1st.arebac.gpeval.GPEval; import org.junit.jupiter.api.Test; -public class FriendOfFriendEvaluateTest { +class FriendOfFriendEvaluateTest { private static final String FRIEND_EDGE_TYPE = "friend"; private static final String USER_NODE_TYPE = "user"; + private static final String GOOD_FRIEND_EDGE_ATTRIBUTE = "goodFriend"; private InMemoryGraphNode outsider = new InMemoryGraphNode("out", USER_NODE_TYPE, Map.of()); private InMemoryGraphNode connector = new InMemoryGraphNode("con", USER_NODE_TYPE, Map.of()); @@ -44,16 +46,40 @@ public class FriendOfFriendEvaluateTest { */ public FriendOfFriendEvaluateTest() { // outsider and connector are both friends - InMemoryGraphEdge outsiderEdge = new InMemoryGraphEdge(outsider, connector, "out->con", FRIEND_EDGE_TYPE, Map.of()); - InMemoryGraphEdge outsiderBackEdge = new InMemoryGraphEdge(connector, outsider, "con->out", FRIEND_EDGE_TYPE, Map.of()); + // outsider considers connector to be a good friend but not vice-versa + InMemoryGraphEdge outsiderEdge = new InMemoryGraphEdge( + outsider, connector, "out->con", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(true)) + ); + InMemoryGraphEdge outsiderBackEdge = new InMemoryGraphEdge( + connector, outsider, "con->out", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(false)) + ); - InMemoryGraphEdge connectorFriendEdge = new InMemoryGraphEdge(connector, connectorFriend, "con->conFriend", FRIEND_EDGE_TYPE, Map.of()); - InMemoryGraphEdge connectorFriendBackEdge = new InMemoryGraphEdge(connectorFriend, connector, "conFriend->con", FRIEND_EDGE_TYPE, Map.of()); + // connector and connectorFriend are (mutually) good friends + InMemoryGraphEdge connectorFriendEdge = new InMemoryGraphEdge( + connector, connectorFriend, "con->conFriend", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(true)) + ); + InMemoryGraphEdge connectorFriendBackEdge = new InMemoryGraphEdge( + connectorFriend, connector, "conFriend->con", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(true)) + ); - InMemoryGraphEdge connectorCompletorEdge = new InMemoryGraphEdge(connector, triangleCompletor, "con->completor", FRIEND_EDGE_TYPE, Map.of()); - InMemoryGraphEdge connectorCompletorBackEdge = new InMemoryGraphEdge(triangleCompletor, connector, "completor->con", FRIEND_EDGE_TYPE, Map.of()); + // completor considers connector to be good friend but not vice-versa + InMemoryGraphEdge connectorCompletorEdge = new InMemoryGraphEdge( + connector, triangleCompletor, "con->completor", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(false)) + ); + InMemoryGraphEdge connectorCompletorBackEdge = new InMemoryGraphEdge( + triangleCompletor, connector, "completor->con", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(true)) + ); - InMemoryGraphEdge connectorFriendToTriangleEdge = new InMemoryGraphEdge(connectorFriend, triangleCompletor, "con->completor", FRIEND_EDGE_TYPE, Map.of()); + InMemoryGraphEdge connectorFriendToTriangleEdge = new InMemoryGraphEdge( + connectorFriend, triangleCompletor, "con->completor", FRIEND_EDGE_TYPE, + Map.of(GOOD_FRIEND_EDGE_ATTRIBUTE, attribute(false)) + ); graph = new InMemoryGraph( List.of(outsider, connector, connectorFriend, triangleCompletor), List.of( @@ -72,12 +98,26 @@ void evaluateOutsider() { assertEquals(Set.of(List.of(triangleCompletor), List.of(connectorFriend)), result); } + @Test + void evaluateGoodOutsiderFriends() { + GraphPattern pattern = createGoodFriendOfFriendRequirement(outsider.id()); + Set> result = GPEval.evaluate(graph, pattern); + assertEquals(Set.of(List.of(connectorFriend)), result); + } + @Test void evaluateConnector() { GraphPattern pattern = createFriendOfFriendPattern(connector.id()); Set> result = GPEval.evaluate(graph, pattern); assertEquals(Set.of(List.of(triangleCompletor)), result); } + + @Test + void evaluateGoodConnectorFriends() { + GraphPattern pattern = createGoodFriendOfFriendRequirement(connector.id()); + Set> result = GPEval.evaluate(graph, pattern); + assertEquals(Set.of(), result); + } @Test void evaluateCompletor() { @@ -85,6 +125,13 @@ void evaluateCompletor() { Set> result = GPEval.evaluate(graph, pattern); assertEquals(Set.of(List.of(outsider), List.of(connectorFriend)), result); } + + @Test + void evaluateGoodCompletorFriends() { + GraphPattern pattern = createGoodFriendOfFriendRequirement(triangleCompletor.id()); + Set> result = GPEval.evaluate(graph, pattern); + assertEquals(Set.of(List.of(connectorFriend)), result); + } // adaptation from example 17 of https://doi.org/10.1145/3401027 private GraphPattern createFriendOfFriendPattern(String requestorId) { @@ -107,4 +154,17 @@ private GraphPattern createFriendOfFriendPattern(String requestorId) { Map.of("requestor", requestor, FRIEND_EDGE_TYPE, friend, "friendOfFriend", friendOfFriend) ); } + + private GraphPattern createGoodFriendOfFriendRequirement(String requestorId) { + GraphPattern friendOfFriendPattern = createFriendOfFriendPattern(requestorId); + Map> newEdgeRequirements = + friendOfFriendPattern + .graph() + .outgoingEdges() + .values() + .stream() + .flatMap(List::stream) + .collect(Collectors.toMap(e -> e, e -> List.of(new AttributeRequirement(GOOD_FRIEND_EDGE_ATTRIBUTE, EQUAL, attribute(true))))); + return new GraphPattern(friendOfFriendPattern.graph(), friendOfFriendPattern.mutualExclusionConstraints(), friendOfFriendPattern.nodeRequirements(), newEdgeRequirements, friendOfFriendPattern.returnedNodes(), friendOfFriendPattern.actorsToNodes()); + } }