/** * Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT * All rights reserved. Use is subject to license terms. See LICENSE.TXT */ package org.diirt.datasource.timecache; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.diirt.datasource.timecache.Data; import org.diirt.datasource.timecache.DataChunk; import org.diirt.datasource.timecache.DataRequestListener; import org.diirt.datasource.timecache.DataRequestThread; import org.diirt.datasource.timecache.PVCacheImpl; import org.diirt.datasource.timecache.PVCacheListener; import org.diirt.datasource.timecache.impl.SimpleFileDataSource; import org.diirt.datasource.timecache.impl.SimpleMemoryStorage; import org.diirt.datasource.timecache.source.DataSource; import org.diirt.datasource.timecache.storage.DataStorage; import org.diirt.datasource.timecache.util.IntervalsList; import org.diirt.util.time.TimeInterval; import org.junit.Assert; import org.junit.Test; /** * Test {@link PVCacheImpl}: retrieves samples from sources for a given time * interval and stores them. * @author Fred Arnaud (Sopra Group) - ITER */ public class PVCacheUnitTest { private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); private class PVCacheListenerCounter implements PVCacheListener { private final AtomicInteger newDataCount = new AtomicInteger(0); private final Instant start; private final Instant end; public PVCacheListenerCounter() { this.start = null; this.end = null; } public PVCacheListenerCounter(Instant start, Instant end) { this.start = start; this.end = end; } @Override public void newDataInCache(SortedSet<Data> newData, TimeInterval newDataInterval, IntervalsList completedIntervals) { if (this.start == null && this.end == null) { newDataCount.getAndAdd(newData.size()); return; } for (Data d : newData) { if (d.getTimestamp().compareTo(start) >= 0 && d.getTimestamp().compareTo(end) <= 0) { newDataCount.addAndGet(1); } } } @Override public void updatedCompletedIntervals(IntervalsList completedIntervals) { // TODO Auto-generated method stub } public int getCount() { return newDataCount.get(); } public void reset() { newDataCount.getAndSet(0); } } private class DataRequestListenerCounter implements DataRequestListener { private final AtomicInteger newDataRequestThreadCount = new AtomicInteger(0); private final AtomicBoolean intervalCompleted = new AtomicBoolean(false); @Override public void newData(DataChunk chunk, DataRequestThread thread) { newDataRequestThreadCount.addAndGet(chunk.getDatas().size()); } @Override public void intervalComplete(DataRequestThread thread) { intervalCompleted.getAndSet(true); } public int getCount() { return newDataRequestThreadCount.get(); } public boolean hasCompleted() { return intervalCompleted.get(); } public void reset() { newDataRequestThreadCount.getAndSet(0); intervalCompleted.getAndSet(false); } } /** * Test that PV cache retrieves samples in the requested interval and does * not request sources again if asking for the same interval. Test that no * sample is lost when retrieved from storage. */ @Test public void testRetrieveSequential() { List<DataSource> sourcesList = new ArrayList<DataSource>(); sourcesList.add(new SimpleFileDataSource( "src/test/resources/archive-ramps-1W.csv", 100)); DataStorage storage = new SimpleMemoryStorage(100); try { PVCacheImpl cache = new PVCacheImpl("TEST-BTY0:RAMP2", sourcesList, storage); Instant start = dateFormat.parse("2014-11-28 00:00").toInstant(); Instant end = dateFormat.parse("2014-11-29 00:00").toInstant(); PVCacheListenerCounter pvCache_counter = new PVCacheListenerCounter(); cache.addListener(pvCache_counter); cache.retrieveDataAsync(TimeInterval.between(start, end)); Assert.assertTrue(cache.isProcessingSources()); int limit = 0; while (cache.isProcessingSources() && limit <= 60) { // 30s try { Thread.sleep(500); } catch (InterruptedException e) { } limit++; } IntervalsList completedIntervals = cache.getCompletedIntervalsList(); // assert that cache has completed the requested interval Assert.assertTrue(completedIntervals.contains(TimeInterval.between(start, end))); Assert.assertEquals(43200, pvCache_counter.getCount()); pvCache_counter.reset(); DataRequestListenerCounter requestThread_counter = new DataRequestListenerCounter(); // ask for smaller inner interval and verify no source is requested start = dateFormat.parse("2014-11-28 12:00").toInstant(); end = dateFormat.parse("2014-11-29 00:00").toInstant(); DataRequestThread thread = cache.retrieveDataAsync(TimeInterval.between(start, end)); Assert.assertFalse(cache.isProcessingSources()); thread.addListener(requestThread_counter); thread.run(); Assert.assertEquals(0, pvCache_counter.getCount()); Assert.assertEquals(21600, requestThread_counter.getCount()); Assert.assertEquals(true, requestThread_counter.hasCompleted()); // ask for overlapping interval and verify both source & storage are requested pvCache_counter.reset(); requestThread_counter.reset(); start = dateFormat.parse("2014-11-27 12:00").toInstant(); end = dateFormat.parse("2014-11-28 12:00").toInstant(); thread = cache.retrieveDataAsync(TimeInterval.between(start, end)); Assert.assertTrue(cache.isProcessingSources()); limit = 0; while (cache.isProcessingSources() && limit <= 60) { // 30s try { Thread.sleep(500); } catch (InterruptedException e) { } limit++; } thread.addListener(requestThread_counter); thread.run(); completedIntervals = cache.getCompletedIntervalsList(); // assert cache has completed the requested interval Assert.assertTrue(completedIntervals.contains(TimeInterval.between( dateFormat.parse("2014-11-27 12:00").toInstant(), dateFormat.parse("2014-11-29 00:00").toInstant()))); Assert.assertEquals(64800, (((SimpleMemoryStorage) ((PVCacheImpl) cache).getStorage())).getStoredSampleCount()); Assert.assertEquals(21600, pvCache_counter.getCount()); Assert.assertEquals(43200, requestThread_counter.getCount()); Assert.assertEquals(true, requestThread_counter.hasCompleted()); // ask for interval before previous ones and verify only source is requested pvCache_counter.reset(); requestThread_counter.reset(); start = dateFormat.parse("2014-11-27 00:00").toInstant(); end = dateFormat.parse("2014-11-27 12:00").toInstant(); thread = cache.retrieveDataAsync(TimeInterval.between(start, end)); Assert.assertTrue(cache.isProcessingSources()); limit = 0; while (cache.isProcessingSources() && limit <= 60) { // 30s try { Thread.sleep(500); } catch (InterruptedException e) { } limit++; } thread.addListener(requestThread_counter); thread.run(); Assert.assertEquals(21600, pvCache_counter.getCount()); Assert.assertEquals(21600, requestThread_counter.getCount()); Assert.assertEquals(true, requestThread_counter.hasCompleted()); } catch (Exception e) { e.printStackTrace(); Assert.fail(e.getMessage()); } } /** * Test that PV cache retrieves samples in the requested interval and does * not request sources again if asking for the same interval. Test that no * sample is lost when retrieved from storage. */ @Test public void testRetrieveParallel() { List<DataSource> sourcesList = new ArrayList<DataSource>(); sourcesList.add(new SimpleFileDataSource( "src/test/resources/archive-ramps-1W.csv", 100)); DataStorage storage = new SimpleMemoryStorage(100); try { PVCacheImpl cache = new PVCacheImpl("TEST-BTY0:RAMP2", sourcesList, storage); final Instant start1 = dateFormat.parse("2014-11-28 00:00").toInstant(); final Instant end1 = dateFormat.parse("2014-11-29 00:00").toInstant(); final Instant start2 = dateFormat.parse("2014-11-28 06:00").toInstant(); final Instant end2 = dateFormat.parse("2014-11-28 12:00").toInstant(); PVCacheListenerCounter pvCache_counter1 = new PVCacheListenerCounter(start1, end1); PVCacheListenerCounter pvCache_counter2 = new PVCacheListenerCounter(start2, end2); DataRequestListenerCounter requestThread_counter1 = new DataRequestListenerCounter(); DataRequestListenerCounter requestThread_counter2 = new DataRequestListenerCounter(); cache.addListener(pvCache_counter1); DataRequestThread thread1 = cache.retrieveDataAsync(TimeInterval.between(start1, end1)); thread1.addListener(requestThread_counter1); thread1.start(); cache.addListener(pvCache_counter2); DataRequestThread thread2 = cache.retrieveDataAsync(TimeInterval.between(start2, end2)); thread2.addListener(requestThread_counter2); thread2.start(); int limit = 0; while (limit <= 60) { // 60s try { if (!cache.isProcessingSources() && requestThread_counter1.hasCompleted() && requestThread_counter2.hasCompleted()) break; Thread.sleep(1000); } catch (InterruptedException e) { } limit++; } if (limit > 60) Assert.fail("Timeout"); Assert.assertEquals(43200, pvCache_counter1.getCount()); Assert.assertEquals(10800, pvCache_counter2.getCount()); Assert.assertEquals(0, requestThread_counter1.getCount()); Assert.assertEquals(0, requestThread_counter2.getCount()); } catch (Exception e) { e.printStackTrace(); Assert.fail(e.getMessage()); } } }