/*-
*******************************************************************************
* Copyright (c) 2011, 2016 Diamond Light Source Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Gerring - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.dawnsci.nexus.test.util;
import static org.eclipse.dawnsci.nexus.builder.data.NexusDataBuilder.ATTR_NAME_AXES;
import static org.eclipse.dawnsci.nexus.builder.data.NexusDataBuilder.ATTR_NAME_SIGNAL;
import static org.eclipse.dawnsci.nexus.builder.data.NexusDataBuilder.ATTR_NAME_TARGET;
import static org.eclipse.dawnsci.nexus.builder.data.NexusDataBuilder.ATTR_SUFFIX_INDICES;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Iterator;
import org.eclipse.dawnsci.analysis.api.tree.Attribute;
import org.eclipse.dawnsci.analysis.api.tree.DataNode;
import org.eclipse.dawnsci.analysis.api.tree.GroupNode;
import org.eclipse.dawnsci.analysis.api.tree.Node;
import org.eclipse.dawnsci.analysis.api.tree.NodeLink;
import org.eclipse.dawnsci.analysis.api.tree.SymbolicNode;
import org.eclipse.dawnsci.analysis.api.tree.TreeFile;
import org.eclipse.dawnsci.nexus.NXdata;
import org.eclipse.dawnsci.nexus.NXobject;
import org.eclipse.dawnsci.nexus.NXroot;
import org.eclipse.january.DatasetException;
import org.eclipse.january.dataset.DTypeUtils;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.ILazyDataset;
import org.eclipse.january.dataset.PositionIterator;
public class NexusAssert {
public static void assertNexusTreesEqual(final TreeFile expectedTree, final TreeFile actualTree) throws Exception {
assertGroupNodesEqual("/", expectedTree.getGroupNode(), actualTree.getGroupNode());
}
public static void assertNodesEquals(String path, final Node expectedNode, final Node actualNode) throws Exception {
if (expectedNode.isGroupNode()) {
assertTrue(path, actualNode.isGroupNode());
assertGroupNodesEqual(path, (GroupNode) expectedNode, (GroupNode) actualNode);
} else if (expectedNode.isDataNode()) {
assertTrue(path, actualNode.isDataNode());
assertDataNodesEqual(path, (DataNode) expectedNode, (DataNode) actualNode);
} else if (expectedNode.isSymbolicNode()) {
assertTrue(path, actualNode.isSymbolicNode());
assertSymbolicNodesEqual(path, (SymbolicNode) expectedNode, (SymbolicNode) actualNode);
} else {
fail("Unknown node type"); // sanity check, shouldn't be possible
}
}
public static void assertGroupNodesEqual(String path,
final GroupNode expectedGroup, final GroupNode actualGroup) throws Exception {
if (expectedGroup == actualGroup) {
return; // same object, trivial case
}
// check the groups have the same NXclass
if (expectedGroup instanceof NXobject) {
assertTrue(path, actualGroup instanceof NXobject);
assertEquals(path, ((NXobject) expectedGroup).getNXclass(), ((NXobject) actualGroup).getNXclass());
}
// check numbers of data nodes and group nodes are the same
assertEquals(path, expectedGroup.getNumberOfDataNodes(), actualGroup.getNumberOfDataNodes());
assertEquals(path, expectedGroup.getNumberOfGroupNodes(), actualGroup.getNumberOfGroupNodes());
// check number of attributes same (i.e. actualGroup has no additional attributes)
// The additional attribute "target" is allowed.
int expectedNumAttributes = expectedGroup.getNumberOfAttributes();
if (expectedGroup.containsAttribute("target") && !actualGroup.containsAttribute("target")) {
expectedNumAttributes--;
}
assertEquals(path, expectedNumAttributes, actualGroup.getNumberOfAttributes());
// check attribute properties same for each attribute
Iterator<String> attributeNameIterator = expectedGroup.getAttributeNameIterator();
while (attributeNameIterator.hasNext()) {
String attributeName = attributeNameIterator.next();
String attrPath = path + Node.ATTRIBUTE + attributeName;
Attribute expectedAttr = expectedGroup.getAttribute(attributeName);
Attribute actualAttr = actualGroup.getAttribute(attributeName);
if (!expectedAttr.getName().equals("target") && !expectedAttr.getName().equals("file_name")) {
assertNotNull(attrPath, actualAttr);
assertAttributesEquals(attrPath, expectedAttr, actualAttr);
}
}
// check child nodes same
final Iterator<String> nodeNameIterator = expectedGroup.getNodeNameIterator();
while (nodeNameIterator.hasNext()) {
String nodeName = nodeNameIterator.next();
String nodePath = path + "/" + nodeName;
// node is either a group node or data node
if (expectedGroup.containsGroupNode(nodeName)) {
assertTrue(nodePath, actualGroup.containsGroupNode(nodeName));
assertGroupNodesEqual(nodePath, expectedGroup.getGroupNode(nodeName), actualGroup.getGroupNode(nodeName));
} else if (expectedGroup.containsDataNode(nodeName)) {
// node is a data node
assertTrue(nodePath, actualGroup.containsDataNode(nodeName));
assertDataNodesEqual(nodePath, expectedGroup.getDataNode(nodeName), actualGroup.getDataNode(nodeName));
} else if (expectedGroup.containsSymbolicNode(nodeName)) {
assertTrue(nodePath, actualGroup.containsSymbolicNode(nodeName));
// assertSymbolicNodesEqual(nodePath, expectedGroup.getDataNode(nodeName), actualGroup.getDataNode(nodeName));
// TODO merge this into a single assertNodesEqual method, which delegates
// to the appropriate method
}
}
}
public static void assertAttributesEquals(final String path, final Attribute expectedAttr,
final Attribute actualAttr) {
assertEquals(path, expectedAttr.getName(), actualAttr.getName());
assertEquals(path, expectedAttr.getTypeName(), actualAttr.getTypeName());
assertEquals(path, expectedAttr.getFirstElement(), actualAttr.getFirstElement());
assertEquals(path, expectedAttr.getSize(), actualAttr.getSize());
if (expectedAttr.getSize() == 1 && expectedAttr.getRank() == 1 && actualAttr.getRank() == 0) {
// TODO fix examples now that we can save scalar (or zero-ranked) datasets
actualAttr.getValue().setShape(1);
}
assertEquals(path, expectedAttr.getRank(), actualAttr.getRank());
assertArrayEquals(path, expectedAttr.getShape(), actualAttr.getShape());
assertDatasetsEqual(path, expectedAttr.getValue(), actualAttr.getValue());
}
public static void assertDataNodesEqual(final String path,
final DataNode expectedDataNode, final DataNode actualDataNode) {
// check number of attributes same (i.e. actualDataNode has no additional attributes)
// additional attribute "target" is allowed, this is added automatically when saving the file
int expectedNumAttributes = expectedDataNode.getNumberOfAttributes();
if (expectedDataNode.containsAttribute("target") && !actualDataNode.containsAttribute("target")) {
expectedNumAttributes--;
}
assertEquals(expectedNumAttributes, actualDataNode.getNumberOfAttributes());
// check attributes properties same for each attribute
Iterator<String> attributeNameIterator = expectedDataNode.getAttributeNameIterator();
while (attributeNameIterator.hasNext()) {
String attributeName = attributeNameIterator.next();
String attrPath = path + Node.ATTRIBUTE + attributeName;
Attribute expectedAttr = expectedDataNode.getAttribute(attributeName);
Attribute actualAttr = actualDataNode.getAttribute(attributeName);
if (!expectedAttr.getName().equals("target")) {
assertNotNull(attrPath, expectedAttr);
assertAttributesEquals(attrPath, expectedAttr, actualAttr);
}
}
assertEquals(path, expectedDataNode.getTypeName(), actualDataNode.getTypeName());
assertEquals(path, expectedDataNode.isAugmented(), actualDataNode.isAugmented());
assertEquals(path, expectedDataNode.isString(), actualDataNode.isString());
assertEquals(path, expectedDataNode.isSupported(), actualDataNode.isSupported());
assertEquals(path, expectedDataNode.isUnsigned(), actualDataNode.isUnsigned());
assertEquals(path, expectedDataNode.getMaxStringLength(), actualDataNode.getMaxStringLength());
// TODO reinstate lines below and check why they break - dataNode2 is null
// assertArrayEquals(path, dataNode1.getMaxShape(), dataNode2.getMaxShape());
// assertArrayEquals(path, dataNode1.getChunkShape(), dataNode2.getChunkShape());
assertEquals(path, expectedDataNode.getString(), actualDataNode.getString());
assertDatasetsEqual(path, expectedDataNode.getDataset(), actualDataNode.getDataset());
}
public static void assertDatasetsEqual(final String path, final ILazyDataset expectedDataset,
final ILazyDataset actualDataset) {
// Note: dataset names can be different, as long as the containing data node names are the same
// assertEquals(dataset1.getName(), dataset2.getName());
// assertEquals(dataset1.getClass(), dataset2.getClass());
assertEquals(path, expectedDataset.getElementClass(), actualDataset.getElementClass());
assertEquals(path, expectedDataset.getElementsPerItem(), actualDataset.getElementsPerItem());
assertEquals(path, expectedDataset.getSize(), actualDataset.getSize());
if (expectedDataset.getSize() == 1 && expectedDataset.getRank() == 1 && actualDataset.getRank() == 0) {
// TODO fix examples now that we can save scalar (or zero-ranked) datasets
actualDataset.setShape(1);
}
assertEquals(path, expectedDataset.getRank(), actualDataset.getRank());
assertArrayEquals(path, expectedDataset.getShape(), actualDataset.getShape());
assertDatasetDataEqual(path, expectedDataset, actualDataset);
// TODO: in future also check metadata
}
private static void assertDatasetDataEqual(final String path,
final ILazyDataset expectedDataset, final ILazyDataset actualDataset) {
if (expectedDataset instanceof Dataset && actualDataset instanceof Dataset) {
assertEquals(path, expectedDataset, actualDataset); // uses Dataset.equals() method
} else {
assertEquals(expectedDataset.getSize(), actualDataset.getSize());
if (expectedDataset.getSize() == 0) {
return;
}
// getSlice() with no args loads whole dataset if a lazy dataset
IDataset expectedSlice;
IDataset actualSlice;
try {
expectedSlice = expectedDataset.getSlice();
actualSlice = actualDataset.getSlice();
} catch (DatasetException e) {
throw new AssertionError("Could not get data from lazy dataset", e.getCause());
}
final int datatype = DTypeUtils.getDType(actualDataset);
PositionIterator positionIterator = new PositionIterator(actualDataset.getShape());
while (positionIterator.hasNext()) {
int[] position = positionIterator.getPos();
switch (datatype) {
case Dataset.BOOL:
assertEquals(path, expectedSlice.getBoolean(position), actualSlice.getBoolean(position));
break;
case Dataset.INT8:
assertEquals(path, expectedSlice.getByte(position), actualSlice.getByte(position));
break;
case Dataset.INT32:
assertEquals(path, expectedSlice.getInt(position), actualSlice.getInt(position));
break;
case Dataset.INT64:
assertEquals(path, expectedSlice.getLong(position), actualSlice.getLong(position));
break;
case Dataset.FLOAT32:
assertEquals(path, expectedSlice.getFloat(position), actualSlice.getFloat(position), 1e-7);
break;
case Dataset.FLOAT64:
assertEquals(path, expectedSlice.getDouble(position), actualSlice.getDouble(position), 1e-15);
break;
case Dataset.STRING:
case Dataset.DATE:
assertEquals(path, expectedSlice.getString(position), actualSlice.getString(position));
break;
case Dataset.COMPLEX64:
case Dataset.COMPLEX128:
case Dataset.OBJECT:
assertEquals(path, expectedSlice.getObject(position), actualSlice.getObject(position));
break;
}
}
}
}
public static void assertSymbolicNodesEqual(final String path,
final SymbolicNode expectedSymbolicNode, final SymbolicNode actualSymbolicNode) {
assertEquals(path, expectedSymbolicNode, actualSymbolicNode);
}
public static void assertSignal(NXdata nxData, String expectedSignalFieldName) {
Attribute signalAttr = nxData.getAttribute(ATTR_NAME_SIGNAL);
assertThat(signalAttr, is(notNullValue()));
assertThat(signalAttr.getRank(), is(0));
assertThat(signalAttr.getFirstElement(), is(equalTo(expectedSignalFieldName)));
assertThat(nxData.getNode(expectedSignalFieldName), is(notNullValue()));
}
public static void assertAxes(NXdata nxData, String... expectedValues) {
Attribute axesAttr = nxData.getAttribute(ATTR_NAME_AXES);
assertThat(axesAttr, is(notNullValue()));
assertThat(axesAttr.getRank(), is(1));
assertThat(axesAttr.getShape()[0], is(expectedValues.length));
IDataset value = axesAttr.getValue();
for (int i = 0; i < expectedValues.length; i++) {
assertThat(value.getString(i), is(equalTo(expectedValues[i])));
}
}
public static void assertShape(NXdata nxData, String fieldName, int... expectedShape) {
DataNode dataNode = nxData.getDataNode(fieldName);
assertThat(fieldName, is(notNullValue()));
int[] actualShape = dataNode.getDataset().getShape();
assertArrayEquals(expectedShape, actualShape);
}
public static void assertIndices(NXdata nxData, String axisName, int... indices) {
Attribute indicesAttr = nxData.getAttribute(axisName + ATTR_SUFFIX_INDICES);
assertThat(indicesAttr, is(notNullValue()));
assertThat(indicesAttr.getRank(), is(1));
assertThat(indicesAttr.getShape()[0], is(indices.length));
IDataset value = indicesAttr.getValue();
for (int i = 0; i < indices.length; i++) {
assertThat(value.getInt(i), is(equalTo(indices[i])));
}
}
public static void assertTarget(NXdata nxData, String destName, NXroot nxRoot, String targetPath) {
DataNode dataNode = nxData.getDataNode(destName);
assertThat(dataNode, is(notNullValue()));
Attribute targetAttr = dataNode.getAttribute(ATTR_NAME_TARGET);
assertThat(targetAttr, is(notNullValue()));
assertThat(targetAttr.getSize(), is(1));
assertThat(targetAttr.getFirstElement(), is(equalTo(targetPath)));
NodeLink nodeLink = nxRoot.findNodeLink(targetPath);
assertTrue(nodeLink.isDestinationData());
assertThat(nodeLink.getDestination(), is(sameInstance(dataNode)));
}
}