package org.graalvm.compiler.graph.test.graphio;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.graalvm.graphio.GraphOutput;
import org.graalvm.graphio.GraphStructure;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
public final class NodeEncodingTest {
private ByteArrayOutputStream out;
@Before
public void initOutput() {
out = new ByteArrayOutputStream();
}
@Test
public void version40TheNodeIsntDumpedWithItsID() throws Exception {
runTheNodeIsntDumpedWithItsID(true);
}
@Test
public void defaultVersionTheNodeIsntDumpedWithItsID() throws Exception {
runTheNodeIsntDumpedWithItsID(false);
}
private void runTheNodeIsntDumpedWithItsID(boolean explicitVersion) throws Exception {
WritableByteChannel w = Channels.newChannel(out);
MockGraph graph = new MockGraph();
MockNodeClass clazz = new MockNodeClass("clazz");
MockNode node = new MockNode(clazz, 33);
try (GraphOutput<MockGraph, ?> dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(4, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) {
dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node));
dump.endGroup();
}
assertEquals("Node is always requested", 1, node.nodeRequested);
assertEquals("Nobody asks for id of a node in version 4.0", 0, node.idTested);
assertByte(false, out.toByteArray(), 33);
assertEquals("Node class of the node has been requested", 1, node.nodeClassRequested);
assertEquals("Node class template name stored", 1, clazz.nameTemplateQueried);
assertFalse("No to string ops", node.toStringRequested);
}
@Test
public void dumpingNodeInVersion10() throws Exception {
runTheNodeIsTreatedAsString(true);
}
private void runTheNodeIsTreatedAsString(boolean explicitVersion) throws Exception {
WritableByteChannel w = Channels.newChannel(out);
MockGraph graph = new MockGraph();
MockNodeClass clazz = new MockNodeClass("clazz");
MockNode node = new MockNode(clazz, 33);
try (GraphOutput<MockGraph, ?> dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(1, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) {
dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node));
dump.endGroup();
}
assertEquals("Node is always requested", 1, node.nodeRequested);
assertEquals("Nobody asks for id of a node in version 1.0", 0, node.idTested);
assertByte(false, out.toByteArray(), 33);
assertEquals("Node class was needed to find out it is not a NodeClass instance", 1, node.nodeClassRequested);
assertEquals("Node class template name wasn't needed however", 0, clazz.nameTemplateQueried);
assertTrue("Node sent as a string version 1.0", node.toStringRequested);
}
@Test
public void dumpingNodeInVersion15() throws Exception {
runTheNodeIsTreatedPoolEntry(true);
}
private void runTheNodeIsTreatedPoolEntry(boolean explicitVersion) throws Exception {
WritableByteChannel w = Channels.newChannel(out);
MockGraph graph = new MockGraph();
MockNodeClass clazz = new MockNodeClass("clazz");
MockNode node = new MockNode(clazz, 33);
try (GraphOutput<MockGraph, ?> dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(5, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) {
dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node));
dump.endGroup();
}
assertEquals("Node is always requested", 1, node.nodeRequested);
assertEquals("Id of our node is requested in version 5.0", 1, node.idTested);
assertByte(true, out.toByteArray(), 33);
assertTrue("Node class was needed at least once", 1 <= node.nodeClassRequested);
assertEquals("Node class template name sent to server", 1, clazz.nameTemplateQueried);
assertFalse("Node.toString() isn't needed", node.toStringRequested);
}
@Test
public void dumpingNodeTwiceInVersion4() throws Exception {
WritableByteChannel w = Channels.newChannel(out);
MockGraph graph = new MockGraph();
MockNodeClass clazz = new MockNodeClass("clazz");
MockNode node = new MockNode(clazz, 33);
try (GraphOutput<MockGraph, ?> dump = GraphOutput.newBuilder(new MockStructure()).protocolVersion(4, 0).build(w)) {
Map<String, Object> props = new LinkedHashMap<>();
props.put("node1", node);
props.put("node2", node);
props.put("node3", node);
dump.beginGroup(graph, "test1", "t1", null, 0, props);
dump.endGroup();
}
assertEquals("Node requested three times", 3, node.nodeRequested);
assertEquals("Nobody asks for id of a node in version 4.0", 0, node.idTested);
assertByte(false, out.toByteArray(), 1, 0, 3);
assertEquals("Node class of the node has been requested three times", 3, node.nodeClassRequested);
assertEquals("Node class template name stored", 1, clazz.nameTemplateQueried);
assertFalse("No to string ops", node.toStringRequested);
}
private static void assertByte(boolean shouldBeFound, byte[] arr, int... value) {
boolean found = false;
int at = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value[at]) {
if (++at == value.length) {
found = true;
break;
}
} else {
at = 0;
}
}
if (shouldBeFound == found) {
return;
}
if (shouldBeFound) {
fail("Value " + value + " not found in\n" + Arrays.toString(arr));
} else {
fail("Value " + value + " surprisingly found in\n" + Arrays.toString(arr));
}
}
private static final class MockStructure implements GraphStructure<MockGraph, MockNode, MockNodeClass, MockNodeClass> {
@Override
public MockGraph graph(MockGraph currentGraph, Object obj) {
return obj instanceof MockGraph ? (MockGraph) obj : null;
}
@Override
public Iterable<? extends MockNode> nodes(MockGraph graph) {
return Collections.emptyList();
}
@Override
public int nodesCount(MockGraph graph) {
return 0;
}
@Override
public int nodeId(MockNode node) {
node.idTested++;
return node.id;
}
@Override
public boolean nodeHasPredecessor(MockNode node) {
return false;
}
@Override
public void nodeProperties(MockGraph graph, MockNode node, Map<String, ? super Object> properties) {
}
@Override
public MockNode node(Object obj) {
if (obj instanceof MockNode) {
((MockNode) obj).nodeRequested++;
return (MockNode) obj;
}
return null;
}
@Override
public MockNodeClass nodeClass(Object obj) {
if (obj instanceof MockNode) {
((MockNode) obj).nodeClassRequested++;
}
return obj instanceof MockNodeClass ? (MockNodeClass) obj : null;
}
@Override
public MockNodeClass classForNode(MockNode n) {
n.nodeClassRequested++;
return n.clazz;
}
@Override
public String nameTemplate(MockNodeClass nodeClass) {
nodeClass.nameTemplateQueried++;
return "";
}
@Override
public Object nodeClassType(MockNodeClass nodeClass) {
return nodeClass.getClass();
}
@Override
public MockNodeClass portInputs(MockNodeClass nodeClass) {
return nodeClass;
}
@Override
public MockNodeClass portOutputs(MockNodeClass nodeClass) {
return nodeClass;
}
@Override
public int portSize(MockNodeClass port) {
return 0;
}
@Override
public boolean edgeDirect(MockNodeClass port, int index) {
return false;
}
@Override
public String edgeName(MockNodeClass port, int index) {
return null;
}
@Override
public Object edgeType(MockNodeClass port, int index) {
return null;
}
@Override
public Collection<? extends MockNode> edgeNodes(MockGraph graph, MockNode node, MockNodeClass port, int index) {
return null;
}
}
private static final class MockGraph {
}
private static final class MockNode {
final MockNodeClass clazz;
final int id;
int idTested;
int nodeClassRequested;
int nodeRequested;
boolean toStringRequested;
MockNode(MockNodeClass clazz, int id) {
this.clazz = clazz;
this.id = id;
}
@Override
public String toString() {
this.toStringRequested = true;
return "MockNode{" + "id=" + id + ", class=" + clazz + '}';
}
}
private static final class MockNodeClass {
final String name;
int nameTemplateQueried;
MockNodeClass(String name) {
this.name = name;
}
@Override
public String toString() {
return "MockNodeClass{" + "name=" + name + '}';
}
}
}