/*-
*******************************************************************************
* 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.remotedataset.test.server;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import org.eclipse.dawnsci.analysis.api.tree.GroupNode;
import org.eclipse.dawnsci.analysis.dataset.function.Downsample;
import org.eclipse.dawnsci.hdf5.nexus.NexusFileFactoryHDF5;
import org.eclipse.dawnsci.nexus.INexusFileFactory;
import org.eclipse.dawnsci.nexus.NexusFile;
import org.eclipse.dawnsci.plotting.api.histogram.IImageService;
import org.eclipse.dawnsci.plotting.api.histogram.ImageServiceBean;
import org.eclipse.dawnsci.remotedataset.ServiceHolder;
import org.eclipse.dawnsci.remotedataset.server.DataServer;
import org.eclipse.dawnsci.remotedataset.test.mock.ImageServiceMock;
import org.eclipse.dawnsci.remotedataset.test.mock.LoaderServiceMock;
import org.eclipse.dawnsci.remotedataset.test.mock.PlotImageServiceMock;
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.DataEvent;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.IDataListener;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.ILazyWriteableDataset;
import org.eclipse.january.dataset.IDatasetConnector;
import org.eclipse.january.dataset.LazyWriteableDataset;
import org.eclipse.january.dataset.Random;
import org.eclipse.swt.graphics.ImageData;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
/**
*
* <pre>
* To run this test from the IDE or it's subsclasses:
* o Run as junit test (will fail)
* o Add all org.eclipse.jetty and javax.serlvet to classpath
* o Set LD_LIBRARY_PATH(linux) or PATH(windows) to:
* ${project_loc:uk.ac.diamond.CBFlib}/lib/${target.os}-${target.arch};${project_loc:hdf.hdf5lib}/lib/${target.os}-${target.arch}
* </pre>
*
* @author Matthew Gerring
*
*/
public class DataServerTest {
protected static INexusFileFactory factory;
protected static DataServer server;
protected static String testDir;
protected static int port;
/**
* Programmatically start the DataServer OSGi application which runs
* under Jetty and starts jetty itself.
* @throws Exception
*/
@BeforeClass
public static void startDataServer() throws Exception {
factory = new NexusFileFactoryHDF5();
// Sorry but the concrete classes for these services are not part of an eclipse project.
// To get these concrete services go to dawnsci.org and follow the instructions for
// setting up dawnsci to run in your application.
ServiceHolder.setDownService(new Downsample());
ServiceHolder.setImageService(new ImageServiceMock());
ServiceHolder.setPlotImageService(new PlotImageServiceMock());
// Start the DataServer
port = TestUtils.getFreePort(8080);
server = new DataServer();
server.setPort(port);
server.start();
System.out.println("Started DataServer on port "+port);
File pluginDir = new File((new File("")).getAbsolutePath()); // Assuming test run in test plugin
testDir = (new File(pluginDir, "testfiles")).getAbsolutePath();
}
@Before
public void setLoader() {
if (ServiceHolder.getLoaderService()==null) {
ServiceHolder.setLoaderService(new LoaderServiceMock(factory));
}
}
@AfterClass
public static void stop() {
server.stop();
}
protected volatile boolean testIsRunning = false;
protected File startHDF5WritingThread() throws IOException, InterruptedException {
return startHDF5WritingThread(1000L);
}
protected final static int LIMIT = 23;
protected File startHDF5WritingThread(final long sleepTime) throws IOException, InterruptedException {
final File ret = File.createTempFile("temp_transient_file", ".h5");
ret.deleteOnExit();
final Thread runner = new Thread(new Runnable() {
public void run() {
ILazyWriteableDataset writer = null;
try (NexusFile file = factory.newNexusFile(ret.getAbsolutePath(), false)) {
file.openToWrite(true); // DO NOT COPY!
GroupNode par = file.getGroup("/entry/data", true); // DO NOT COPY!
final int[] shape = new int[] { 1, 64, 64 };
final int[] max = new int[] { -1, 64, 64 };
writer = new LazyWriteableDataset("image", Dataset.FLOAT, shape, max, shape, null); // DO NOT COPY!
file.createData(par, writer);
int index = 0;
while (testIsRunning) {
int[] start = { index, 0, 0 };
int[] stop = { index + 1, 64, 64 };
index++;
if (index > LIMIT)
index = LIMIT; // Stall on the last image to avoid writing massive stacks
IDataset rimage = Random.rand(new int[] { 1, 64, 64 });
rimage.setName("image");
writer.setSlice(new IMonitor.Stub(), rimage, start, stop, null);
// file.flush(); // remove explicit flush
System.err.println("> HDF5 wrote image to " + ret);
System.err.println("> New shape " + Arrays.toString(writer.getShape()));
Thread.sleep(sleepTime);
}
} catch (Exception ne) {
ne.printStackTrace();
}
}
});
runner.setPriority(Thread.MIN_PRIORITY);
runner.setDaemon(true);
runner.start();
// Wait for a bit to ensure file is being written
Thread.sleep(2*sleepTime);
return ret;
}
protected File startFileWritingThread(final long waitTime, final boolean dir) throws IOException, InterruptedException {
final File ret = dir
? new File(File.createTempFile("temp_transient_file", ".png").getParentFile(), "test")
: File.createTempFile("temp_transient_file", ".png");
ret.deleteOnExit();
if (dir) {
if (ret.exists()) TestUtils.recursiveDelete(ret.toPath());
ret.mkdir();
}
final Thread runner = new Thread(new Runnable() {
public void run() {
int index = 0;
while(testIsRunning) {
try {
IDataset rimage = Random.rand(new int[]{64, 64});
IImageService iservice = ServiceHolder.getImageService();
ImageServiceBean bean = iservice.createBeanFromPreferences();
bean.setImage(rimage);
final ImageData data = iservice.getImageData(bean);
final BufferedImage bi = iservice.getBufferedImage(data);
File file = dir
? new File(ret, "image_"+index+".png")
: ret;
if (file.exists()) {
file.delete();
}
file.deleteOnExit();
index++;
if (index > LIMIT)
index = LIMIT; // Stall on the last image to avoid writing massive stacks
ImageIO.write(bi, "PNG", file);
Thread.sleep(waitTime);
System.err.println(">> Thread wrote "+file.getAbsolutePath());
} catch (Exception ne) {
ne.printStackTrace();
break;
}
}
}
});
runner.setPriority(Thread.MIN_PRIORITY);
runner.setDaemon(true);
runner.start();
// Wait for a bit to ensure file is being written
Thread.sleep(2*waitTime);
return ret;
}
protected void checkAndWait(final IDatasetConnector data, long time, long imageTime) throws Exception {
final int count = (int)time/(int)imageTime;
checkAndWait(data, time, imageTime, count-6);
}
protected void checkAndWait(final IDatasetConnector data, long time, long imageTime, int min) throws Exception {
final int count = (int)time/(int)imageTime;
try {
final List<DataEvent> events = new ArrayList<DataEvent>(count);
// Check that we get events about the image changing.
data.addDataListener(new IDataListener() {
@Override
public void dataChangePerformed(DataEvent evt) {
try {
System.err.println("Data changed, shape is "+Arrays.toString(evt.getShape()));
if (!Arrays.equals(evt.getShape(), data.getDataset().getShape())) {
throw new Exception("Data shape and event shape are not the same!");
}
events.add(evt);
} catch (Exception ne) {
ne.printStackTrace();
}
}
});
Thread.sleep(time);
if (events.size() < min) throw new Exception("Less data events than expected! Event count was "+events.size()+" Min expected was "+min);
} finally {
data.disconnect();
}
}
}