/*- ******************************************************************************* * Copyright (c) 2011, 2014 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.hdf5.model.internal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.dawnsci.hdf.object.HierarchicalDataFactory; import org.eclipse.dawnsci.hdf.object.HierarchicalDataUtils; import org.eclipse.dawnsci.hdf.object.IHierarchicalDataFile; import org.eclipse.dawnsci.hdf5.model.IHierarchicalDataFileModel; import org.eclipse.january.dataset.Dataset; import org.junit.Test; public class HierarchicalDataFileModelTest { private static final IHierarchicalDataFileGetReader get_i05_4859_Reader = new IHierarchicalDataFileGetReader() { @Override public IHierarchicalDataFile getReader() throws Exception { return HierarchicalDataFactory.getReader(new File("testfiles/i05-4859.nxs").getAbsolutePath()); } }; /** * Used to debug the file structure to help write the other tests */ @Test public void printDataContents() throws Exception { try (IHierarchicalDataFile reader = get_i05_4859_Reader.getReader()) { printDataContents(reader); } } private void printDataContents(IHierarchicalDataFile reader) throws Exception { Map<String, Object> attributeValues = reader.getAttributeValues(); for (Entry<String, Object> entry : attributeValues.entrySet()) { String ln = entry.getKey() + "=" + entry.getValue() + "==" + HierarchicalDataUtils.extractScalar(entry.getValue()); System.out.println(ln); } String g = reader.getRoot(); printGroup(reader, g); } private void printGroup(IHierarchicalDataFile reader, String g) throws Exception { List<String> members = reader.memberList(g); int n = members.size(); for (int i = 0; i < n; i++) { String childPath = members.get(i); System.out.print(childPath); if (reader.isDataset(childPath)) { hdf.object.Dataset dataset = (hdf.object.Dataset) reader.getData(childPath); Object value = dataset.read(); System.out.print("=DIMS("); System.out.print(Arrays.toString(dataset.getDims())); System.out.print(")"); System.out.print(value); System.out.print("==" + HierarchicalDataUtils.extractScalar(value)); } else if (reader.isGroup(childPath)) { printGroup(reader, childPath); } System.out.println(); } } @Test public void testInvalidFile() { IHierarchicalDataFileGetReader getInvalidFileReader = new IHierarchicalDataFileGetReader() { @Override public IHierarchicalDataFile getReader() throws Exception { String absolutePath = "/tmp/non-existent-file.nxs"; return HierarchicalDataFactory.getReader(absolutePath); } }; IHierarchicalDataFileModel invalidFileModel = new HierarchicalDataFileModel( getInvalidFileReader); assertEquals(null, invalidFileModel.getPath("/@HDF5_Version")); } @Test public void testGetPath() { IHierarchicalDataFileModel model = new HierarchicalDataFileModel( get_i05_4859_Reader); checkGetPathOnAllCases(model); } @Test public void testHasPath() { IHierarchicalDataFileModel model = new HierarchicalDataFileModel( get_i05_4859_Reader); checkHasPathOnAllCases(model); } private void checkGetPathOnAllCases(IHierarchicalDataFileModel model) { // global attributes assertEquals("1.8.7", model.getPath("/@HDF5_Version")); // path with scalar dataset assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); // scalar dataset attribute assertEquals("Hz", model.getPath("/entry1/instrument/analyser/cps@units")); // group assertEquals(null, model.getPath("/entry1")); // attrib on group assertEquals("NXentry", model.getPath("/entry1@NX_class")); // path with non 1-size dataset assertEquals(null, model.getPath("/entry1/instrument/analyser/energies")); // attrib on non 1-size dataset, string assertEquals("3", model.getPath("/entry1/instrument/analyser/energies@axis")); // attrib on non 1-size dataset, integer assertEquals(1, model.getPath("/entry1/instrument/analyser/energies@primary")); // non-existent root attrib assertEquals(null, model.getPath("/@NonExistent")); // non-existent attrib on valid path assertEquals(null, model.getPath("/entry1/instrument/analyser/cps@NonExistent")); // non-existent path assertEquals(null, model.getPath("/entry1/instrument/analyser/NonExistent")); // non-existent attrib on group assertEquals(null, model.getPath("/entry1@NonExistent")); } private void checkHasPathOnAllCases(IHierarchicalDataFileModel model) { // root attrib assertTrue(model.hasPath("/@HDF5_Version")); // path with scalar dataset assertTrue(model.hasPath("/entry1/instrument/analyser/cps")); // attrib scalar dataset assertTrue(model.hasPath("/entry1/instrument/analyser/cps@units")); // group assertTrue(model.hasPath("/entry1")); // attrib on group assertTrue(model.hasPath("/entry1@NX_class")); // path with non 1-size dataset assertTrue(model.hasPath("/entry1/instrument/analyser/energies")); // attrib on non 1-size dataset, string assertTrue(model.hasPath("/entry1/instrument/analyser/energies@axis")); // attrib on non 1-size dataset, integer assertTrue(model .hasPath("/entry1/instrument/analyser/energies@primary")); // non-existent root attrib assertTrue(!model.hasPath("/@NonExistent")); // non-existent attrib on valid path assertTrue(!model .hasPath("/entry1/instrument/analyser/cps@NonExistent")); // non-existent path assertTrue(!model.hasPath("/entry1/instrument/analyser/NonExistent")); // non-existent attrib on group assertTrue(!model.hasPath("/entry1@NonExistent")); } /** * This test makes sure that re-reading the same attribute does not cause a * new file access. * <p> * This depends on the overall contract of the Model not being violated in * the code, that is that the reader is not left open between calls to the * model. * <p> * Additionally, this test may fail if more data is cached on the initial * read of the Nexus file. For example, at one point each attribute was * fetched individually from the underlying reader. However on discovering * that reading any one attribute loaded all the attributes on that node a * new method was added * {@link IHierarchicalDataFile#getAttributeValues(String)} to get all the * attributes in one go. */ @Test public void hitsCache() { final int[] count = new int[] { 0 }; IHierarchicalDataFileGetReader getCountingReader = new IHierarchicalDataFileGetReader() { @Override public IHierarchicalDataFile getReader() throws Exception { count[0] += 1; return get_i05_4859_Reader.getReader(); } }; IHierarchicalDataFileModel model = new HierarchicalDataFileModel( getCountingReader); // Make sure re-reading attribute (either has or value) does not // increase file access count assertTrue(model.hasPath("/@HDF5_Version")); assertEquals(1, count[0]); assertTrue(model.hasPath("/@HDF5_Version")); assertEquals(1, count[0]); assertEquals("1.8.7", model.getPath("/@HDF5_Version")); assertEquals(1, count[0]); // Make sure reading additional attributes on same node does not // increase file access count assertTrue(model.hasPath("/@NeXus_version")); assertEquals(1, count[0]); assertEquals("4.3.1", model.getPath("/@NeXus_version")); assertEquals(1, count[0]); // Make sure reading additional non-existent attributes on same // node does not increase file access count assertTrue(!model.hasPath("/@NonExistentAttribute")); assertEquals(1, count[0]); assertEquals(null, model.getPath("/@NonExistentAttribute")); assertEquals(1, count[0]); // Make sure re-reading dataset (either has or value) does not // increase file access count assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); assertEquals(2, count[0]); assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); assertEquals(2, count[0]); assertTrue(model.hasPath("/entry1/instrument/analyser/cps")); assertEquals(2, count[0]); // Make sure oscillating between attribs and dataset on same // path does not increase counts assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); assertEquals(2, count[0]); assertEquals("Hz", model.getPath("/entry1/instrument/analyser/cps@units")); assertEquals(3, count[0]); assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); assertEquals(3, count[0]); assertEquals("Hz", model.getPath("/entry1/instrument/analyser/cps@units")); assertEquals(3, count[0]); // Make sure reading additional non-existent dataset on same // node does not increase file access count assertTrue(!model.hasPath("/Non/Existent/Path")); assertEquals(4, count[0]); assertEquals(null, model.getPath("/Non/Existent/Path")); assertEquals(4, count[0]); // Make sure reading additional non-existent path's attributes // on same node does not increase file access count assertTrue(!model.hasPath("/Another/Non/Existent/Path@Attrib1")); assertEquals(5, count[0]); assertEquals(null, model.getPath("/Another/Non/Existent/Path@Attrib1")); assertEquals(5, count[0]); assertTrue(!model.hasPath("/Another/Non/Existent/Path@Attrib2")); assertEquals(5, count[0]); assertEquals(null, model.getPath("/Another/Non/Existent/Path@Attrib2")); assertEquals(5, count[0]); // Re-use check of all cases to make sure re-reading each/all of them // does not increase access count checkGetPathOnAllCases(model); checkHasPathOnAllCases(model); final int expectedCount = count[0]; checkGetPathOnAllCases(model); checkHasPathOnAllCases(model); assertEquals(expectedCount, count[0]); } @Test public void testReadAttribFirst() { IHierarchicalDataFileModel model = new HierarchicalDataFileModel( get_i05_4859_Reader); assertEquals("Hz", model.getPath("/entry1/instrument/analyser/cps@units")); assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); } @Test public void testReadDatasetFirst() { IHierarchicalDataFileModel model = new HierarchicalDataFileModel( get_i05_4859_Reader); assertEquals(1754416.0, model.getPath("/entry1/instrument/analyser/cps")); assertEquals("Hz", model.getPath("/entry1/instrument/analyser/cps@units")); } private Object createNexusFile(final int dtype, final long[] shape, final Object buffer) throws Exception { String PATH = "kichwa1"; final File file = File.createTempFile("HierarchicalDataFileModelTest", ".nxs"); file.delete(); try { // Create a file and verify it IHierarchicalDataFile writer = HierarchicalDataFactory.getWriter(file.getAbsolutePath()); writer.createDataset(PATH, dtype, shape, buffer, writer.getRoot()); writer.close(); try (IHierarchicalDataFile reader = HierarchicalDataFactory .getReader(file.getAbsolutePath());) { printDataContents(reader); } IHierarchicalDataFileModel model = new HierarchicalDataFileModel( new IHierarchicalDataFileGetReader() { @Override public IHierarchicalDataFile getReader() throws Exception { String absolutePath = file.getAbsolutePath(); return HierarchicalDataFactory .getReader(absolutePath); } }); return model.getPath(PATH); } finally { file.delete(); } } /** * This test was created to test support Datasets that have > 1 dimension, * but the sizes of all the dimensions are 1. e.g. /entry1/sample/name is * stored as [ZrTe3_X60] with dims [1,1] and previously was not supported * * @throws Exception */ @Test public void testMultiDimArrayString() throws Exception { final String GOLD = "Gold"; assertEquals(GOLD, createNexusFile(Dataset.STRING, new long[] { 1 }, new String[] { GOLD })); assertEquals( GOLD, createNexusFile(Dataset.STRING, new long[] { 1, 1 }, new String[] { GOLD })); assertEquals( GOLD, createNexusFile(Dataset.STRING, new long[] { 1, 1, 1 }, new String[] { GOLD })); assertEquals( GOLD, createNexusFile(Dataset.STRING, new long[] { 1, 1, 1, 1 }, new String[] { GOLD })); } @Test public void testMultiDimArrayFloat() throws Exception { assertEquals(1.0, createNexusFile(Dataset.FLOAT64, new long[] { 1 }, new double[] { 1.0 })); assertEquals( 1.0, createNexusFile(Dataset.FLOAT64, new long[] { 1, 1 }, new double[][] { { 1.0 } })); assertEquals( 1.0, createNexusFile(Dataset.FLOAT64, new long[] { 1, 1, 1 }, new double[][][] { { { 1.0 } } })); assertEquals( 1.0, createNexusFile(Dataset.FLOAT64, new long[] { 1, 1 }, new double[] { 1.0 })); assertEquals( 1.0, createNexusFile(Dataset.FLOAT64, new long[] { 1, 1, 1 }, new double[] { 1.0 })); } }