package org.epics.archiverappliance.etl; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.data.DBRTimeEvent; import org.epics.archiverappliance.data.SampleValue; import org.epics.archiverappliance.data.ScalarStringSampleValue; import org.epics.archiverappliance.data.ScalarValue; import org.epics.archiverappliance.data.VectorStringSampleValue; import org.epics.archiverappliance.data.VectorValue; import org.epics.archiverappliance.engine.membuf.ArrayListEventStream; import org.epics.archiverappliance.etl.conversion.ThruNumberAndStringConversion; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import org.epics.archiverappliance.utils.simulation.SimulationEventStream; import org.epics.archiverappliance.utils.simulation.SimulationValueGenerator; import org.junit.After; import org.junit.Before; import org.junit.Test; import edu.stanford.slac.archiverappliance.PB.data.DBR2PBTypeMapping; /** * Test various type conversion functions. * Some type conversions are trivial; others are more complex. * We generate some sample events of a particular type; then convert to other dest types and convert back. * @author mshankar * */ public class ThruNumberAndStringConversionFunctionTest { private static Logger logger = Logger.getLogger(ThruNumberAndStringConversionFunctionTest.class.getName()); @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } @Test public void testThruNumberConversion() throws Exception { testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_INT, ArchDBRTypes.DBR_SCALAR_DOUBLE); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_ENUM, ArchDBRTypes.DBR_SCALAR_DOUBLE); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_FLOAT, ArchDBRTypes.DBR_SCALAR_DOUBLE); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_ENUM, ArchDBRTypes.DBR_SCALAR_INT); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_INT, ArchDBRTypes.DBR_SCALAR_ENUM); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_DOUBLE, ArchDBRTypes.DBR_SCALAR_ENUM); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_DOUBLE, ArchDBRTypes.DBR_SCALAR_INT); testThruNumberConversionForDBRType(ArchDBRTypes.DBR_SCALAR_DOUBLE, ArchDBRTypes.DBR_SCALAR_FLOAT); } private void testThruNumberConversionForDBRType(ArchDBRTypes srcDBRType, ArchDBRTypes destDBRType) throws Exception { logger.info("Testing conversion from " + srcDBRType.toString() + " to " + destDBRType.toString()); EventStream srcStream = generateDataForArchDBRType(srcDBRType); EventStream destStream = convertToType(srcStream, destDBRType); compareStreams(srcStream, destStream); } private EventStream generateDataForArchDBRType(ArchDBRTypes dbrType) throws Exception { int numEvents = 500; ArrayListEventStream ret = new ArrayListEventStream(numEvents, new RemotableEventStreamDesc(dbrType, "test", TimeUtils.getCurrentYear())); int eventsAdded = 0; Constructor<? extends DBRTimeEvent> serializingConstructor = DBR2PBTypeMapping.getPBClassFor(dbrType).getSerializingConstructor(); try(SimulationEventStream simstream = new SimulationEventStream(dbrType, new ValueGenerator(dbrType, numEvents))) { for(Event simEvent : simstream) { DBRTimeEvent genEvent = (DBRTimeEvent) serializingConstructor.newInstance(simEvent); if(eventsAdded % 1000 == 0) { genEvent.addFieldValue("HIHI", "Test"); genEvent.addFieldValue("LOLO", "13:40:12"); if(eventsAdded % 2000 == 0) { genEvent.markAsActualChange(); } } ret.add(genEvent); if(eventsAdded++ > numEvents) break; } return ret; } } private EventStream convertToType(EventStream srcStream, ArchDBRTypes destDBRType) throws IOException { return new ThruNumberAndStringConversion(destDBRType).convertStream(srcStream); } private void compareStreams(EventStream srcStream, EventStream destStream) throws IOException { Iterator<Event> srcIt = srcStream.iterator(); Iterator<Event> destIt = destStream.iterator(); int eventCount = 0; while(srcIt.hasNext() || destIt.hasNext()) { DBRTimeEvent srcEvent = (DBRTimeEvent) srcIt.next(); DBRTimeEvent destEvent = (DBRTimeEvent) destIt.next(); assertTrue("Compare timestamps failed at event " + eventCount, srcEvent.getEventTimeStamp().equals(destEvent.getEventTimeStamp())); assertTrue("Compare status failed at event " + eventCount, srcEvent.getStatus() == destEvent.getStatus()); assertTrue("Compare severity failed at event " + eventCount, srcEvent.getSeverity() == destEvent.getSeverity()); assertTrue("Compare value failed at event " + eventCount + " " + srcEvent.getSampleValue().getValue().doubleValue() + " " + destEvent.getSampleValue().getValue().doubleValue() + " " + Math.abs(srcEvent.getSampleValue().getValue().doubleValue() - destEvent.getSampleValue().getValue().doubleValue()), Math.abs(srcEvent.getSampleValue().getValue().doubleValue() - destEvent.getSampleValue().getValue().doubleValue()) < 0.0005); assertTrue("Compare fields failed at event " + eventCount, compareMaps(srcEvent.getFields(), destEvent.getFields())); assertTrue("Compare fields changed failed at event " + eventCount, srcEvent.isActualChange() == destEvent.isActualChange()); eventCount++; } } private static boolean compareMaps(HashMap<String, String> map1, HashMap<String, String> map2) { Set<String> removedKeys = new HashSet<String>(map1.keySet()); removedKeys.removeAll(map2.keySet()); Set<String> addedKeys = new HashSet<String>(map2.keySet()); addedKeys.removeAll(map1.keySet()); Set<Entry<String, String>> changedEntries = new HashSet<Entry<String, String>>(map2.entrySet()); changedEntries.removeAll(map1.entrySet()); return removedKeys.isEmpty() && addedKeys.isEmpty() && changedEntries.isEmpty(); } private class ValueGenerator implements SimulationValueGenerator { private int numSamples; private ArchDBRTypes dbrType; public ValueGenerator(ArchDBRTypes dbrType, int numSamples) { this.numSamples = numSamples; this.dbrType = dbrType; } @Override public int getNumberOfSamples(ArchDBRTypes type) { return numSamples; } @Override public SampleValue getSampleValue(ArchDBRTypes type, int secondsIntoYear) { switch(dbrType) { case DBR_SCALAR_BYTE: return new ScalarValue<Byte>((byte) secondsIntoYear); case DBR_SCALAR_DOUBLE: return new ScalarValue<Double>((double) secondsIntoYear); case DBR_SCALAR_ENUM: return new ScalarValue<Short>((short) secondsIntoYear); case DBR_SCALAR_FLOAT: return new ScalarValue<Float>((float) secondsIntoYear); case DBR_SCALAR_INT: return new ScalarValue<Integer>((int) secondsIntoYear); case DBR_SCALAR_SHORT: return new ScalarValue<Short>((short) secondsIntoYear); case DBR_SCALAR_STRING: return new ScalarStringSampleValue(Integer.toString(secondsIntoYear)); case DBR_V4_GENERIC_BYTES: return new ScalarStringSampleValue(Integer.toString(secondsIntoYear)); case DBR_WAVEFORM_BYTE: return new VectorValue<Byte>(Collections.nCopies(10*secondsIntoYear, ((byte)(secondsIntoYear%255)))); case DBR_WAVEFORM_DOUBLE: return new VectorValue<Double>(Collections.nCopies(10*secondsIntoYear, ((double)secondsIntoYear))); case DBR_WAVEFORM_ENUM: return new VectorValue<Short>(Collections.nCopies(10*secondsIntoYear, ((short)secondsIntoYear))); case DBR_WAVEFORM_FLOAT: return new VectorValue<Float>(Collections.nCopies(10*secondsIntoYear, ((float)secondsIntoYear))); case DBR_WAVEFORM_INT: return new VectorValue<Integer>(Collections.nCopies(10*secondsIntoYear, ((int)secondsIntoYear))); case DBR_WAVEFORM_SHORT: return new VectorValue<Short>(Collections.nCopies(10*secondsIntoYear, ((short)secondsIntoYear))); case DBR_WAVEFORM_STRING: return new VectorStringSampleValue(Collections.nCopies(10*secondsIntoYear, Integer.toString(secondsIntoYear))); default: throw new UnsupportedOperationException(); } } } }