/*-
* Copyright © 2009 Diamond Light Source Ltd., Science and Technology
* Facilities Council
*
* This file is part of GDA.
*
* GDA is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 3 as published by the Free
* Software Foundation.
*
* GDA is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with GDA. If not, see <http://www.gnu.org/licenses/>.
*/
package gda;
import gda.configuration.properties.LocalProperties;
import gda.data.nexus.INeXusInfoWriteable;
import gda.data.nexus.extractor.NexusExtractor;
import gda.data.nexus.extractor.NexusGroupData;
import gda.data.nexus.tree.INexusTree;
import gda.data.nexus.tree.NexusTreeNode;
import gda.data.nexus.tree.NexusTreeProvider;
import gda.device.Detector;
import gda.device.DeviceException;
import gda.device.Scannable;
import gda.device.detector.NXDetectorData;
import gda.device.detector.NexusDetector;
import gda.factory.Factory;
import gda.factory.FactoryException;
import gda.factory.Findable;
import gda.jython.InterfaceProvider;
import gda.jython.MockJythonServerFacade;
import gda.observable.IObserver;
import gda.util.TestUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.dawnsci.analysis.api.tree.Attribute;
import org.eclipse.dawnsci.analysis.api.tree.Node;
import org.eclipse.dawnsci.nexus.NexusException;
import org.eclipse.dawnsci.nexus.NexusFile;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.ShapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collection of utility functions to assist testing.
*/
public class TestHelpers {
/**
* Sets up of environment for the a test Set property so that output is to Nexus format file
* Uses MockJythonServerFacade and MockJythonServer to configure InterfaceProvider
*
* @param testClass
* e.g. gda.data.nexus.ScanToNexusTest
* @param nameOfTest
* name of test method which the testClass e.g. testCreateScanFile
* @param makedir
* if true the scratch dir is deleted and constructed
* @return The directory into which output will be sent
* @throws Exception
* if setup fails
*/
public static String setUpTest(Class<?> testClass, String nameOfTest, boolean makedir) throws Exception {
String testScratchDirectoryName = TestUtils.setUpTest(testClass, nameOfTest, makedir);
MockJythonServerFacade mockJythonServerFacade = new MockJythonServerFacade();
InterfaceProvider.setCommandRunnerForTesting(mockJythonServerFacade);
InterfaceProvider.setCurrentScanControllerForTesting(mockJythonServerFacade);
InterfaceProvider.setTerminalPrinterForTesting(mockJythonServerFacade);
InterfaceProvider.setScanStatusHolderForTesting(mockJythonServerFacade);
InterfaceProvider.setJythonNamespaceForTesting(mockJythonServerFacade);
InterfaceProvider.setAuthorisationHolderForTesting(mockJythonServerFacade);
InterfaceProvider.setScriptControllerForTesting(mockJythonServerFacade);
InterfaceProvider.setPanicStopForTesting(mockJythonServerFacade);
InterfaceProvider.setCurrentScanInformationHolderForTesting(mockJythonServerFacade);
InterfaceProvider.setJythonServerNotiferForTesting(mockJythonServerFacade);
InterfaceProvider.setDefaultScannableProviderForTesting(mockJythonServerFacade);
InterfaceProvider.setScanDataPointProviderForTesting(mockJythonServerFacade);
InterfaceProvider.setBatonStateProviderForTesting(mockJythonServerFacade);
InterfaceProvider.setJSFObserverForTesting(mockJythonServerFacade);
LocalProperties.set(LocalProperties.GDA_DATA, testScratchDirectoryName + "/Data");
LocalProperties.set(LocalProperties.GDA_VAR_DIR, testScratchDirectoryName + "/Data");
LocalProperties.set(LocalProperties.GDA_LOGS_DIR, testScratchDirectoryName + "/Data");
LocalProperties.set(LocalProperties.GDA_DATAWRITER_DIR, testScratchDirectoryName + "/Data");
LocalProperties.set("gda.data.scan.datawriter.setTime0", "True");
LocalProperties.set(LocalProperties.GDA_DATA_SCAN_DATAWRITER_DATAFORMAT, "NexusDataWriter");
LocalProperties.set("gda.nexus.instrumentApi", "True");
return testScratchDirectoryName;
}
public static Scannable createTestScannable(String name, Object position, String[] extraNames, String[] inputNames,
int level, String[] outputFormat, String[] units) {
return new SimpleScannable(name, position, extraNames, inputNames, level, outputFormat, units);
}
public static Scannable createTestScannableGaussianXY(String name, Double xPosition, String xName, String yName,
Double center, Double width, Double magnitude, Double pcNoise, int level, String outputFormatx,
String outputFormaty, String unitsx, String unitsy) {
return new GaussianScannableXY(name, xPosition, xName, yName, center, width, magnitude, pcNoise, level,
outputFormatx, outputFormaty, unitsx, unitsy);
}
public static Scannable createTestScannableSine(String name, Double xPosition, Double magnitude, Double pcNoise,
String xName, String yName, int level, String outputFormatx, String outputFormaty, String unitsx,
String unitsy) {
return new SineScannable(name, xPosition, magnitude, pcNoise, xName, yName, level, outputFormatx,
outputFormaty, unitsx, unitsy);
}
public static Detector createTestDetector(String name, Object position, String[] extraNames, String[] inputNames,
int level, String[] outputFormat, NexusGroupData data, String filename, String description,
String detectorID, String detectorType) {
SimpleDetector det = new SimpleDetector(name, position, extraNames, inputNames, level, outputFormat, data,
filename, description, detectorID, detectorType);
return det;
}
public static Detector createTestCounterTimer(String name, Object position, String[] extraNames,
String[] inputNames, int level, String[] outputFormat, NexusGroupData data, String filename,
String description, String detectorID, String detectorType, String[] channelNames) {
return new SimpleCounterTimer(name, position, extraNames, inputNames, level, outputFormat, data, filename,
description, detectorID, detectorType, channelNames);
}
public static Detector createTestFileDetector(String name, int level, String fileNameFormat, int[] dim,
String description, String detectorID, String detectorType) {
return new FileDetector(name, null, new String[] { name }, new String[0], level, new String[] { "%s" },
fileNameFormat, dim, description, detectorID, detectorType);
}
public static NexusDetector createTestISubDetector(String name, Object position, String[] extraNames,
String[] inputNames, int level, String[] outputFormat, NexusGroupData data, String filename,
String description, String detectorID, String detectorType) {
return new SimpleSubDetector(name, position, extraNames, inputNames, level, outputFormat, data, filename,
description, detectorID, detectorType);
}
public static NexusGroupData createTestNexusGroupData(int[] shape, int dtype,
boolean useSuperToString) {
int size = ShapeUtils.calcSize(shape);
Dataset dataset = DatasetFactory.createRange(size, dtype);
dataset.setShape(shape);
NexusGroupData ngd = new Data(dataset, useSuperToString);
ngd.isDetectorEntryData = true;
return ngd;
}
/**
* @param name
* @return Factory implementation that can be used for testing - simply add findables and add to Finder instance
*/
public static Factory createTestFactory(String name) {
return new TestFactory(name);
}
}
class SimpleScannable implements Scannable, INeXusInfoWriteable {
private static final Logger logger = LoggerFactory.getLogger(SimpleScannable.class);
String name;
Object position;
String[] extraNames;
String[] inputNames;
int level;
String[] outputFormat;
String[] units;
SimpleScannable(String name, Object position, String[] extraNames, String[] inputNames, int level,
String[] outputFormat, String[] units) {
this.name = name;
this.position = position;
this.extraNames = extraNames;
this.inputNames = inputNames;
this.level = level;
this.outputFormat = outputFormat;
this.units = units;
}
@Override
public void asynchronousMoveTo(Object position) throws DeviceException {
this.position = position;
}
@Override
public void atEnd() throws DeviceException {
}
@Override
public void atPointEnd() throws DeviceException {
}
@Override
public void atPointStart() throws DeviceException {
}
@Override
public void atScanEnd() throws DeviceException {
}
@Override
public void atScanLineEnd() throws DeviceException {
}
@Override
public void atScanLineStart() throws DeviceException {
}
@Override
public void atScanStart() throws DeviceException {
}
@Override
public void atStart() throws DeviceException {
}
@Override
public void atLevelMoveStart() {
}
@Override
public void atLevelStart() {
}
@Override
public void atLevelEnd() {
}
@Override
public String[] getExtraNames() {
return extraNames;
}
@Override
public String[] getInputNames() {
return inputNames;
}
@Override
public int getLevel() {
return level;
}
@Override
public String[] getOutputFormat() {
return outputFormat;
}
@Override
public Object getPosition() throws DeviceException {
return position;
}
@Override
public boolean isAt(Object positionToTest) throws DeviceException {
return positionToTest.equals(getPosition());
}
@Override
public boolean isBusy() throws DeviceException {
return false;
}
@Override
public String checkPositionValid(Object position) {
return null;
}
@Override
public void moveTo(Object position) throws DeviceException {
asynchronousMoveTo(position);
}
@Override
public void setExtraNames(String[] names) {
this.extraNames = names;
}
@Override
public void setInputNames(String[] names) {
inputNames = names;
}
@Override
public void setLevel(int level) {
this.level = level;
}
@Override
public void setOutputFormat(String[] names) {
outputFormat = names;
}
@Override
public void stop() throws DeviceException {
}
@Override
public void waitWhileBusy() throws DeviceException, InterruptedException {
}
@Override
public void close() throws DeviceException {
}
@Override
public Object getAttribute(String attributeName) throws DeviceException {
return null;
}
@Override
public int getProtectionLevel() throws DeviceException {
return 0;
}
@Override
public void setAttribute(String attributeName, Object value) throws DeviceException {
}
@Override
public void setProtectionLevel(int newLevel) throws DeviceException {
}
@Override
public void addIObserver(IObserver anIObserver) {
}
@Override
public void deleteIObserver(IObserver anIObserver) {
}
@Override
public void deleteIObservers() {
}
@Override
public void reconfigure() throws FactoryException {
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "SimpleScannable:" + getName();
}
@Override
public void writeNeXusInformation(NexusFile file, Node node) throws NexusException {
try {
if (units != null && units.length > 0) {
Dataset ad = DatasetFactory.createFromObject(units[0]);
ad.setName("units");
Attribute a = file.createAttribute(ad);
file.addAttribute(node, a);
}
} catch (NexusException e) {
logger.debug("DOF: Problem writing additional info to NeXus file.");
}
}
@Override
public void atCommandFailure() throws DeviceException {
}
@Override
public String toFormattedString() {
return name + " : " + position.toString();
}
}
class GaussianScannableXY extends SimpleScannable {
Double center, width, magnitude, pcNoise;
GaussianScannableXY(String name, Double xPosition, String xName, String yName, Double center, Double width,
Double magnitude, Double pcNoise, int level, String outputFormatx, String outputFormaty, String unitsx,
String unitsy) {
super(name, xPosition, new String[] { yName }, new String[] { xName }, level, new String[] { outputFormatx,
outputFormaty }, new String[] { unitsx, unitsy });
this.center = center;
this.width = width;
this.magnitude = magnitude;
this.pcNoise = pcNoise;
}
@Override
public Object getPosition() throws DeviceException {
// we assume the position is a double - it is only for testing
Double xval = (Double) super.getPosition();
Double noise = pcNoise;
return new Double[] {
xval,
magnitude * (1 + (0.01 * noise * (Math.random() - 0.5)))
* Math.exp(-((xval - center) * (xval - center)) / width) };
}
@Override
public String toString() {
return "GaussianScannableXY:" + getName();
}
}
class SineScannable extends SimpleScannable {
Double magnitude;
Double noise;
public SineScannable(String name, Double position, Double magnitude, Double pcNoise, String xName, String yName,
int level, String outputFormatx, String outputFormaty, String unitsx, String unitsy) {
super(name, position, new String[] { yName }, new String[] { xName }, level, new String[] { outputFormatx,
outputFormaty }, new String[] { unitsx, unitsy });
this.magnitude = magnitude;
this.noise = pcNoise;
}
@Override
public Object getPosition() throws DeviceException {
Double xval = (Double) super.getPosition();
Double noiseVal = 0.01 * noise * magnitude * Math.random();
return new Double[] { xval, magnitude * Math.sin(xval) + noiseVal };
}
@Override
public String toString() {
return "SineScannable:" + getName();
}
}
class SimpleDetector implements Detector {
String name;
Object position;
String[] extraNames;
String[] inputNames;
int level;
String[] outputFormat;
String description;
String detectorID;
String detectorType;
NexusGroupData data;
String filename;
SimpleDetector(String name, Object position, String[] extraNames, String[] inputNames, int level,
String[] outputFormat, NexusGroupData data, String filename, String description, String detectorID,
String detectorType) {
this.name = name;
this.position = position;
// extraNames should be the length of the dimensions if defined
if (data != null) {
int lengthOfData = 1;
for (int i = 0; i < data.dimensions.length; i++) {
lengthOfData *= data.dimensions[i];
}
if (extraNames.length != lengthOfData) {
if (lengthOfData == 1) {
this.extraNames = new String[] { name };
} else {
this.extraNames = new String[lengthOfData];
for (int i = 0; i < lengthOfData; i++) {
this.extraNames[i] = name + "_" + Integer.toString(i);
}
}
}
} else {
this.extraNames = extraNames;
}
this.inputNames = inputNames;
this.level = level;
this.outputFormat = outputFormat;
this.data = data;
this.description = description;
this.detectorID = detectorID;
this.detectorType = detectorType;
this.filename = filename;
}
@Override
public void asynchronousMoveTo(Object position) throws DeviceException {
this.position = position;
}
@Override
public void atEnd() throws DeviceException {
}
@Override
public void atPointEnd() throws DeviceException {
}
@Override
public void atPointStart() throws DeviceException {
}
@Override
public void atScanEnd() throws DeviceException {
}
@Override
public void atScanLineEnd() throws DeviceException {
}
@Override
public void atScanLineStart() throws DeviceException {
}
@Override
public void atScanStart() throws DeviceException {
}
@Override
public void atStart() throws DeviceException {
}
@Override
public String[] getExtraNames() {
return extraNames;
}
@Override
public String[] getInputNames() {
return inputNames;
}
@Override
public int getLevel() {
return level;
}
@Override
public String[] getOutputFormat() {
return outputFormat;
}
@Override
public Object getPosition() throws DeviceException {
return position;
}
@Override
public boolean isAt(Object positionToTest) throws DeviceException {
return positionToTest.equals(getPosition());
}
@Override
public boolean isBusy() throws DeviceException {
return false;
}
@Override
public String checkPositionValid(Object position) {
return null;
}
@Override
public void moveTo(Object position) throws DeviceException {
asynchronousMoveTo(position);
}
@Override
public void setExtraNames(String[] names) {
this.extraNames = names;
}
@Override
public void setInputNames(String[] names) {
inputNames = names;
}
@Override
public void setLevel(int level) {
this.level = level;
}
@Override
public void setOutputFormat(String[] names) {
outputFormat = names;
}
@Override
public void stop() throws DeviceException {
}
@Override
public void waitWhileBusy() throws DeviceException, InterruptedException {
}
@Override
public void close() throws DeviceException {
}
@Override
public Object getAttribute(String attributeName) throws DeviceException {
return null;
}
@Override
public int getProtectionLevel() throws DeviceException {
return 0;
}
@Override
public void setAttribute(String attributeName, Object value) throws DeviceException {
}
@Override
public void setProtectionLevel(int newLevel) throws DeviceException {
}
@Override
public void addIObserver(IObserver anIObserver) {
}
@Override
public void deleteIObserver(IObserver anIObserver) {
}
@Override
public void deleteIObservers() {
}
@Override
public void reconfigure() throws FactoryException {
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void collectData() throws DeviceException {
}
@Override
public boolean createsOwnFiles() throws DeviceException {
return filename != null;
}
@Override
public void endCollection() throws DeviceException {
}
@Override
public int[] getDataDimensions() throws DeviceException {
return data.dimensions;
}
@Override
public String getDescription() throws DeviceException {
return description;
}
@Override
public String getDetectorID() throws DeviceException {
return detectorID;
}
@Override
public String getDetectorType() throws DeviceException {
return detectorType;
}
@Override
public int getStatus() throws DeviceException {
return 0;
}
@Override
public void prepareForCollection() throws DeviceException {
}
@Override
public Object readout() throws DeviceException {
return filename != null ? filename : data.getBuffer();
}
@Override
public void setCollectionTime(double time) throws DeviceException {
}
@Override
public double getCollectionTime() throws DeviceException {
return 0.;
}
@Override
public String toString() {
return "SimpleDetector:" + getName();
}
@Override
public void atLevelMoveStart() {
}
@Override
public void atLevelStart() {
}
@Override
public void atLevelEnd() {
}
@Override
public void atCommandFailure() throws DeviceException {
}
@Override
public String toFormattedString() {
return name + " : " + position.toString();
}
}
class FileDetector extends SimpleDetector {
private final String filenameFormatString;
private int fileNumber = 0;
private final int[] dim;
FileDetector(String name, Object position, String[] extraNames, String[] inputNames, int level,
String[] outputFormat, String filenameFormatString, int[] dim, String description, String detectorID,
String detectorType) {
super(name, position, extraNames, inputNames, level, outputFormat, null, "", description, detectorID,
detectorType);
this.filenameFormatString = filenameFormatString;
this.dim = dim;
}
@Override
public Object readout() throws DeviceException {
fileNumber += 1;
return String.format(filenameFormatString, fileNumber);
}
@Override
public int[] getDataDimensions() throws DeviceException {
return dim;
}
}
class SimpleCounterTimer extends SimpleDetector implements Detector {
String[] channelNames;
SimpleCounterTimer(String name, Object position, String[] extraNames, String[] inputNames, int level,
String[] outputFormat, NexusGroupData data, String filename, String description, String detectorID,
String detectorType, String[] channelNames) {
super(name, position, extraNames, inputNames, level, outputFormat, data, filename, description, detectorID,
detectorType);
this.channelNames = channelNames;
this.extraNames = channelNames;
}
// @Override
// public double[] readChans() throws DeviceException {
// return (double[]) data.getBuffer();
// }
}
class SimpleSubDetector extends SimpleDetector implements NexusDetector {
SimpleSubDetector(String name, Object position, String[] extraNames, String[] inputNames, int level,
String[] outputFormat, NexusGroupData data, String filename, String description, String detectorID,
String detectorType) {
super(name, position, extraNames, inputNames, level, outputFormat, data, filename, description, detectorID,
detectorType);
}
@Override
public NexusTreeProvider readout() throws DeviceException {
NXDetectorData nxdetData = new TestNXDetectorData();
if (filename != null) {
nxdetData.addData(name, new NexusGroupData(new int[19]));
nxdetData.addFileName(name, filename);
INexusTree detTree = nxdetData.getDetTree(name);
{
NexusTreeNode data = new NexusTreeNode("description", NexusExtractor.SDSClassName, null,
new NexusGroupData("Generic GDA Detector - External Files"));
data.setIsPointDependent(false);
detTree.addChildNode(data);
}
{
NexusTreeNode data = new NexusTreeNode("type", NexusExtractor.SDSClassName, null, new NexusGroupData(
"Detector"));
data.setIsPointDependent(false);
detTree.addChildNode(data);
}
} else {
nxdetData.addData(name, data, null, null);
}
return nxdetData;
}
}
class TestNXDetectorData extends NXDetectorData {
@Override
public String toString() {
NexusGroupData data = getNexusTree().getChildNode("data", NexusExtractor.SDSClassName).getData();
return data.toString();
}
}
class Data extends NexusGroupData {
boolean useSuperToString;
Data(Dataset dataset, boolean useSuperToString) {
super(dataset);
this.useSuperToString = useSuperToString;
}
@Override
public String toString() {
if (useSuperToString) {
return super.toString();
}
Serializable s = getBuffer();
if (s instanceof byte[]) {
String sb = "";
for (byte b : (byte[]) s) {
sb += Byte.toString(b) + ",";
}
return sb.toString();
}
return s.toString();
}
}
class TestFactory implements Factory {
private HashMap<String, Findable> findables = new HashMap<String, Findable>();
public TestFactory(String name) {
setName(name);
}
@Override
public void addFindable(Findable findable) {
findables.put(findable.getName(), findable);
}
@Override
public boolean containsExportableObjects() {
return false;
}
@Override
@SuppressWarnings("unchecked")
public <T extends Findable> T getFindable(String name) throws FactoryException {
return (T) findables.get(name);
}
@Override
public List<String> getFindableNames() {
return null;
}
@Override
public List<Findable> getFindables() {
return new ArrayList<Findable>();
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLocal() {
return true;
}
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void configure() throws FactoryException {
}
}