package edu.stanford.slac.archiverappliance.PlainPB; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Timestamp; import java.text.DecimalFormat; import java.util.Iterator; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.common.BasicContext; import org.epics.archiverappliance.common.POJOEvent; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.config.ArchDBRTypes; import org.epics.archiverappliance.config.ConfigServiceForTests; import org.epics.archiverappliance.config.StoragePluginURLParser; import org.epics.archiverappliance.data.ScalarValue; import org.epics.archiverappliance.engine.membuf.ArrayListEventStream; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * The FileBackedPBEventStream supports two iterators - one is a file-position based one and the other is a time based one. * For performance reasons, we should use the file-position based iterator in cases where the query start time is after the timestamp of the first sample; defaulting to the time based one in case of unexpected circumstances. * This test makes sure we have this behavior; these are the test cases we are expected to test. * <ol> * <li>FKTS - The timestamp of the first sample in the file</li> * <li>LKTS - The timestamp of the last sample in the file</li> * <li>QTS - The starttime of the query/request</li> * <li>QTE - The endtime of the query/request</li> * <li></li> * </ol> * <pre> * FKTS LKTS * 1 - QTS -- QTE | | * 2 - QTS --------|-------- QTE | * 3 - QTS --------|---------------------------------------------------------|-------------------------------- QTE * 4 - | QTS ----------------- QTE | * 5 - | QTS -------------------------------------------|-------------------------------- QTE * 6 - | | QTS ----------------- QTE * * </pre> * * @author mshankar * */ public class FileBackedIteratorTest { private static Logger logger = Logger.getLogger(FileBackedIteratorTest.class.getName()); File testFolder = new File(ConfigServiceForTests.getDefaultPBTestFolder() + File.separator + "FileBackedIteratorTest"); String pvName = ConfigServiceForTests.ARCH_UNIT_TEST_PVNAME_PREFIX + ":FileBackedIteratorTest"; short currentYear = TimeUtils.getCurrentYear(); Path pbFilePath = Paths.get(testFolder.getAbsolutePath(), pvName.replace(":", "/").replace("--", "") + ":" + currentYear + ".pb"); ArchDBRTypes dbrType = ArchDBRTypes.DBR_SCALAR_DOUBLE; PlainPBStoragePlugin storagePlugin; private ConfigServiceForTests configService; private Timestamp FKTS = null; private Timestamp LKTS = null; @Before public void setUp() throws Exception { configService = new ConfigServiceForTests(new File("./bin")); storagePlugin = (PlainPBStoragePlugin) StoragePluginURLParser.parseStoragePlugin("pb://localhost?name=FileBackedIteratorTest&rootFolder=" + testFolder.getAbsolutePath() + "&partitionGranularity=PARTITION_YEAR", configService); // Add data with gaps every month DecimalFormat monthFmt = new DecimalFormat("00"); for(int month = 1; month < 12; month++) { long startOfMonthEpochSeconds = TimeUtils.convertToEpochSeconds(TimeUtils.convertFromISO8601String(currentYear + "-" + monthFmt.format(month+1) + "-01T08:00:00.000Z")); // Generate data for 10 days for(int day = 0; day < 10; day++) { ArrayListEventStream strm = new ArrayListEventStream(86400, new RemotableEventStreamDesc(ArchDBRTypes.DBR_SCALAR_DOUBLE, pvName, currentYear)); for(int second = 0; second < 86400; second+=15) { strm.add(new POJOEvent(ArchDBRTypes.DBR_SCALAR_DOUBLE, TimeUtils.convertFromEpochSeconds(startOfMonthEpochSeconds + day*86400 + second, 0), new ScalarValue<Double>((double)second), 0, 0)); } try(BasicContext context = new BasicContext()) { storagePlugin.appendData(context, pvName, strm); } } } PBFileInfo fileInfo = new PBFileInfo(pbFilePath); FKTS = fileInfo.getFirstEvent().getEventTimeStamp(); LKTS = fileInfo.getLastEvent().getEventTimeStamp(); logger.info("After generating data," + "FKTS = " + TimeUtils.convertToISO8601String(FKTS) + "LKTS = " + TimeUtils.convertToISO8601String(LKTS) ); } @After public void tearDown() throws Exception { FileUtils.deleteDirectory(testFolder); } @Test public void testIterator() throws IOException { makeSureWeGetCorrectIterator("Case 1", TimeUtils.minusDays(FKTS, 60), TimeUtils.minusDays(FKTS, 2), TimeUtils.minusDays(FKTS, 59), FKTS, FileBackedPBEventStreamTimeBasedIterator.class); makeSureWeGetCorrectIterator("Case 2", TimeUtils.minusDays(FKTS, 60), TimeUtils.minusDays(FKTS, 2), TimeUtils.plusDays(FKTS,1), TimeUtils.plusDays(FKTS, 90), FileBackedPBEventStreamPositionBasedIterator.class); makeSureWeGetCorrectIterator("Case 3", TimeUtils.minusDays(FKTS, 60), TimeUtils.minusDays(FKTS, 1), TimeUtils.plusDays(LKTS,1), TimeUtils.plusDays(LKTS, 90), FileBackedPBEventStreamPositionBasedIterator.class); makeSureWeGetCorrectIterator("Case 4", FKTS, TimeUtils.plusDays(FKTS, 60), TimeUtils.minusDays(LKTS,90), TimeUtils.minusDays(LKTS, 1), FileBackedPBEventStreamPositionBasedIterator.class); makeSureWeGetCorrectIterator("Case 5", FKTS, TimeUtils.plusDays(FKTS, 60), LKTS, TimeUtils.plusDays(LKTS, 90), FileBackedPBEventStreamPositionBasedIterator.class); makeSureWeGetCorrectIterator("Case 6", LKTS, TimeUtils.plusDays(LKTS, 10), TimeUtils.plusDays(LKTS,1), TimeUtils.plusDays(LKTS, 90), FileBackedPBEventStreamPositionBasedIterator.class); } /** * Make sure we get the expected iterator * @param testCase * @param minQTS * @param maxQTS * @param expectedIteratorClass */ private void makeSureWeGetCorrectIterator(String testCase, Timestamp minQTS, Timestamp maxQTS, Timestamp minQTE, Timestamp maxQTE, Class<? extends Iterator<Event>> expectedIteratorClass) throws IOException { for(Timestamp QTS = minQTS; QTS.before(maxQTS); QTS = TimeUtils.plusDays(QTS, 1)) { for(Timestamp QTE = minQTE; QTE.before(maxQTE); QTE = TimeUtils.plusDays(QTE, 1)) { if(QTS.equals(QTE) || QTS.after(QTE)) { // logger.info("Skipping " + " for QTS " + TimeUtils.convertToISO8601String(QTS) + " and QTE " + TimeUtils.convertToISO8601String(QTE)); continue; } logger.debug("Checking " + testCase + " for QTS " + TimeUtils.convertToISO8601String(QTS) + " and QTE " + TimeUtils.convertToISO8601String(QTE)); try(FileBackedPBEventStream strm = new FileBackedPBEventStream(pvName, pbFilePath, dbrType, QTS, QTE, false)) { assertTrue("We are not getting the expeected iterator " + expectedIteratorClass.getName() + " for " + testCase + " for QTS " + TimeUtils.convertToISO8601String(QTS) + " and QTE " + TimeUtils.convertToISO8601String(QTE), expectedIteratorClass.isInstance(strm.iterator())); } } } } }