package org.javersion.object;
import static org.assertj.core.api.Assertions.assertThat;
import static org.javersion.object.TestUtil.properties;
import static org.javersion.object.TestUtil.property;
import static org.javersion.path.PropertyPath.ROOT;
import static org.javersion.reflect.TypeDescriptors.getTypeDescriptor;
import java.util.HashMap;
import java.util.Map;
import org.javersion.core.Persistent;
import org.javersion.core.Revision;
import org.javersion.path.PropertyPath;
import org.junit.Test;
public class ReferencesTest {
public static final Object NODE_ALIAS = Persistent.object(getTypeDescriptor(Node.class).getSimpleName());
public static class Node {
@Id public Integer id;
public Node left;
public Node right;
public Node() {}
public Node(int id) {
this.id = id;
}
public int hashCode() {
if (id == null) throw new IllegalStateException("id is null");
return id;
}
public boolean equals(Object obj) {
if (id == null) throw new IllegalStateException("id is null");
if (obj == this) {
return true;
} else if (obj instanceof Node) {
Node other = (Node) obj;
return this.id == other.id;
} else {
return false;
}
}
}
@Versionable
public static class Container {
Map<Integer, Node> nodes = new HashMap<>();
public Container() {}
public Container(Node node) {
nodes.put(node.id, node);
}
public void add(Node node) {
nodes.put(node.id, node);
}
}
public static TypeMappings typeMappings = TypeMappings.builder()
.withClass(Node.class)
.asReferenceForPath("nodes")
.build();
private final ObjectSerializer<Node> nodeSerializer = new ObjectSerializer<>(Node.class, typeMappings);
private final ObjectSerializer<Container> containerSerializer = new ObjectSerializer<>(Container.class, typeMappings);
@Test
public void Node_Cycles_Read_And_Write() {
Node root = new Node(1);
root.left = new Node(2);
root.right = root;
root.left.left = root;
root.left.right = root.left;
Map<PropertyPath, Object> properties = nodeSerializer.toPropertyMap(root);
Map<PropertyPath, Object> expectedProperties = properties(
ROOT, 1l,
property("nodes[1]"), NODE_ALIAS,
property("nodes[1].id"), 1l,
property("nodes[1].left"), 2l,
property("nodes[1].right"), 1l,
property("nodes[2]"), NODE_ALIAS,
property("nodes[2].id"), 2l,
property("nodes[2].left"), 1l,
property("nodes[2].right"), 2l
);
assertThat(properties).isEqualTo(expectedProperties);
root = nodeSerializer.fromPropertyMap(properties);
assertThat(root.id).isEqualTo(1);
assertThat(root.left.id).isEqualTo(2);
assertThat(root.right).isSameAs(root);
assertThat(root.left.left).isSameAs(root);
assertThat(root.left.right).isSameAs(root.left);
}
@Test
public void mapped_references() {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
n1.right = n2.left = n3;
Container container = new Container();
container.add(n1);
container.add(n2);
Map<PropertyPath, Object> properties = containerSerializer.toPropertyMap(container);
container = containerSerializer.fromPropertyMap(properties);
assertThat(container.nodes).hasSize(3);
n3 = container.nodes.get(1).right;
assertThat(container.nodes.get(3)).isSameAs(n3);
assertThat(n3).isSameAs(container.nodes.get(2).left);
}
@Test
public void concurrently_moved_and_nulled_reference() {
Node n1 = new Node(1);
n1.left = new Node(2);
ObjectVersionManager<Node, Void> manager = new ObjectVersionManager<Node, Void>(nodeSerializer, false).init();
Revision rev1 = manager.versionBuilder(n1).build().revision;
// Move child from left to right
n1.right = n1.left;
n1.left = null;
manager.versionBuilder(n1).parents(rev1).build();
// Concurrent version that removes child altogether
n1.right = null;
n1.left = null;
manager.versionBuilder(n1).parents(rev1).build();
MergeObject<Node, Void> merge = manager.mergeBranches();
n1 = merge.object;
assertThat(n1.left).isNull();
assertThat(n1.right).isNull();
assertThat(merge.getConflicts().isEmpty()).isTrue();
}
}