blob: a3c7f90bc9c75ec36fd2aad6b954b8be9acadb09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Ericsson and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.statesystem.core.tests.backend;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.tests.stubs.statevalues.CustomStateValueStub;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
/**
* Abstract class to test implementations of the {@link IStateHistoryBackend}
* interface.
*
* @author Patrick Tasse
* @author Geneviève Bastien
*/
public abstract class StateHistoryBackendTestBase {
/* Some state values of each type */
private static final Object INT_VAL1 = -42;
private static final Object INT_VAL2 = 675893;
private static final Object LONG_VAL1 = -78L;
private static final Object LONG_VAL2 = 2234L;
private static final Object DOUBLE_VAL1 = -9.87;
private static final Object DOUBLE_VAL2 = 50324.131643;
private static final Object STR_VAL1 = "A string";
private static final Object STR_VAL2 = "Another éèstr";
/**
* Gets the backend to be used for building.
*
* @param startTime
* The start time of the history
*
* @return The backend to be used for building.
* @throws IOException
* if an exception occurs
*/
protected abstract IStateHistoryBackend getBackendForBuilding(long startTime) throws IOException;
/**
* Gets the backend to be used for querying. The default implementation
* returns the backend that was used for building.
* <p>
* Only the returned backend should be used after calling this method. The
* one sent in parameter might have been disposed.
*
* @param backend
* The backend that was used for building
* @return The backend to be used for querying.
* @throws IOException
* if an exception occurs
*/
@SuppressWarnings("unused")
protected IStateHistoryBackend getBackendForQuerying(IStateHistoryBackend backend) throws IOException {
return backend;
}
/**
* Prepares a backend to be used in tests. The specified intervals will be
* inserted in the backend, and then the backend will be closed.
*
* @param startTime
* The start time of the history
* @param endTime
* The end time at which to close the history
* @param intervals
* The intervals to insert in the history backend
* @return The backend to be used for querying.
*/
protected final @Nullable IStateHistoryBackend prepareBackend(long startTime, long endTime,
List<ITmfStateInterval> intervals) {
try {
IStateHistoryBackend backend = getBackendForBuilding(startTime);
insertIntervals(backend, intervals);
backend.finishedBuilding(Math.max(endTime, backend.getEndTime()));
return getBackendForQuerying(backend);
} catch (IOException e) {
fail(e.getMessage());
return null;
}
}
/**
* Insert the specified intervals in the provided backend.
*
* @param backend
* The backend to be used
* @param intervals
* The intervals to insert in the history backend
*/
protected static void insertIntervals(IStateHistoryBackend backend, List<ITmfStateInterval> intervals) {
for (ITmfStateInterval interval : intervals) {
backend.insertPastState(interval.getStartTime(), interval.getEndTime(), interval.getAttribute(), interval.getValue());
}
}
/**
* Initializes a list for the number of attributes in the backend and
* associates a null value for each
*
* @param nbAttrib
* The number of attributes in the backend
* @return A list of null values for each attribute
*/
private static List<@Nullable ITmfStateInterval> prepareIntervalList(int nbAttrib) {
List<@Nullable ITmfStateInterval> intervals = new ArrayList<>(nbAttrib);
for (int i = 0; i < nbAttrib; i++) {
intervals.add(null);
}
return intervals;
}
/**
* Test the integrity of a backend by first building the backend with the
* specified intervals, closing it, and then querying at every single
* timestamp, making sure that all returned intervals intersect with the
* query time. The backend start and end time will be checked.
* <p>
* If <code>allowNull</code> is false, the specified intervals must cover
* the full range for all attributes. The method will make sure that no null
* intervals are returned.
*
* @param startTime
* The start time of the history
* @param endTime
* The end time of the history
* @param nbAttr
* The number of attributes
* @param intervals
* The list of intervals to insert
* @param allowNull
* True if null intervals are allowed, false otherwise
* @return The backend to be used for querying.
*/
protected final IStateHistoryBackend buildAndQueryFullRange(long startTime, long endTime, int nbAttr, List<ITmfStateInterval> intervals, boolean allowNull) {
final IStateHistoryBackend backend = prepareBackend(startTime, endTime, intervals);
assertNotNull(backend);
try {
/*
* Query at every valid time stamp, making sure only the expected
* intervals are returned.
*/
for (long t = backend.getStartTime(); t <= backend.getEndTime(); t++) {
List<@Nullable ITmfStateInterval> stateInfo = prepareIntervalList(nbAttr);
backend.doQuery(stateInfo, t);
for (int attr = 0; attr < stateInfo.size(); attr++) {
ITmfStateInterval interval = stateInfo.get(attr);
if (!allowNull) {
assertTrue("null interval at t=" + t + " for attr=" + attr, interval != null);
}
if (interval != null) {
assertTrue(interval + " does not intersect t=" + t, interval.intersects(t));
}
}
}
assertEquals(startTime, backend.getStartTime());
assertEquals(endTime, backend.getEndTime());
} catch (StateSystemDisposedException e) {
fail(e.getMessage());
}
return backend;
}
/**
* Test the full query method by filling a small backend with intervals
* placed in a "stair-like" fashion, like this:
*
* <pre>
* |x----x----x---x|
* |xx----x----x--x|
* |x-x----x----x-x|
* |x--x----x----xx|
* | ... |
* </pre>
*
* and then querying at every single timestamp, making sure all, and only,
* the expected intervals are returned.
*/
@Test
public void testCascadingIntervals() {
final int nbAttr = 10;
final long duration = 10;
final long startTime = 0;
final long endTime = 1000;
List<ITmfStateInterval> intervals = new ArrayList<>();
for (long t = startTime + 1; t <= endTime + duration; t++) {
intervals.add(new TmfStateInterval(
Math.max(startTime, t - duration),
Math.min(endTime, t - 1),
(int) t % nbAttr,
t));
}
buildAndQueryFullRange(startTime, endTime, nbAttr, intervals, false);
}
/**
* Test the full query method by filling a small backend with intervals that
* take the full time range, like this:
*
* <pre>
* |x-------------x|
* |x-------------x|
* |x-------------x|
* |x-------------x|
* | ... |
* </pre>
*
* and then querying at every single timestamp, making sure all, and only,
* the expected intervals are returned.
*/
@Test
public void testFullIntervals() {
final int nbAttr = 1000;
final long startTime = 0;
final long endTime = 1000;
List<ITmfStateInterval> intervals = new ArrayList<>();
for (int attr = 0; attr < nbAttr; attr++) {
intervals.add(new TmfStateInterval(
startTime,
endTime,
attr,
attr));
}
buildAndQueryFullRange(startTime, endTime, nbAttr, intervals, false);
}
/**
* Test inserting values of different types and querying them right after
*/
@Test
public void testInsertQueryStateValues() {
/* Test specific data initialization */
long startTime = 10;
long timeStep = 5;
int intQuark = 0;
int longQuark = 1;
int doubleQuark = 2;
int strQuark = 3;
int customQuark = 4;
/* Register custom factory and create a custom state value */
CustomStateValueStub.registerFactory();
ITmfStateValue customVal = new CustomStateValueStub(10, "a string");
try {
IStateHistoryBackend backend = getBackendForBuilding(startTime);
assertNotNull(backend);
/* Int interval */
backend.insertPastState(startTime, startTime + timeStep, intQuark, INT_VAL1);
ITmfStateInterval interval = backend.doSingularQuery(startTime, intQuark);
assertEquals("Int interval start time", startTime, interval.getStartTime());
assertEquals("Int interval end time", startTime + timeStep, interval.getEndTime());
assertEquals("Int interval value", INT_VAL1, interval.getValue());
/* Long interval */
backend.insertPastState(startTime, startTime + timeStep, longQuark, LONG_VAL1);
interval = backend.doSingularQuery(startTime, longQuark);
assertEquals("Long interval start time", startTime, interval.getStartTime());
assertEquals("Long interval end time", startTime + timeStep, interval.getEndTime());
assertEquals("Long interval value", LONG_VAL1, interval.getValue());
/* Double interval */
backend.insertPastState(startTime, startTime + timeStep, doubleQuark, DOUBLE_VAL1);
interval = backend.doSingularQuery(startTime, doubleQuark);
assertEquals("Double interval start time", startTime, interval.getStartTime());
assertEquals("Double interval end time", startTime + timeStep, interval.getEndTime());
assertEquals("Double interval value", DOUBLE_VAL1, interval.getValue());
/* String interval */
backend.insertPastState(startTime, startTime + timeStep, strQuark, STR_VAL1);
interval = backend.doSingularQuery(startTime, strQuark);
assertEquals("String interval start time", startTime, interval.getStartTime());
assertEquals("String interval end time", startTime + timeStep, interval.getEndTime());
assertEquals("String interval value", STR_VAL1, interval.getValue());
/* Custom state values */
backend.insertPastState(startTime, startTime + timeStep, customQuark, customVal);
interval = backend.doSingularQuery(startTime, customQuark);
assertEquals("Custom interval start time", startTime, interval.getStartTime());
assertEquals("Custom interval end time", startTime + timeStep, interval.getEndTime());
assertEquals("Custom interval value", customVal, interval.getValue());
/*
* Add other intervals for the int quark and query at different
* times
*/
backend.insertPastState(startTime + timeStep + 1, startTime + (2 * timeStep), intQuark, INT_VAL2);
backend.insertPastState(startTime + (2 * timeStep) + 1, startTime + (3 * timeStep), intQuark, INT_VAL1);
interval = backend.doSingularQuery(startTime + timeStep, intQuark);
assertEquals("Int interval value", INT_VAL1, interval.getValue());
interval = backend.doSingularQuery(startTime + timeStep + 1, intQuark);
assertEquals("Int interval value", INT_VAL2, interval.getValue());
interval = backend.doSingularQuery(startTime + (2 * timeStep), intQuark);
assertEquals("Int interval value", INT_VAL2, interval.getValue());
interval = backend.doSingularQuery(startTime + (2 * timeStep) + 1, intQuark);
assertEquals("Int interval value", INT_VAL1, interval.getValue());
} catch (TimeRangeException | StateSystemDisposedException | IOException e) {
fail(e.getMessage());
}
}
/**
* Test querying various state value types after the state system has been
* built and finished
*/
@Test
public void testBuildNowQueryLaterStateValues() {
/* Test specific data initialization */
long startTime = 10;
long timeStep = 5;
int intQuark = 0;
int longQuark = 1;
int doubleQuark = 2;
int strQuark = 3;
int customQuark = 4;
int nbAttribs = 5;
/* Register custom factory and create custom state values */
CustomStateValueStub.registerFactory();
ITmfStateValue customVal = new CustomStateValueStub(10, "a string");
ITmfStateValue customVal2 = new CustomStateValueStub(Short.MAX_VALUE, "another string");
try {
IStateHistoryBackend backend = getBackendForBuilding(startTime);
assertNotNull(backend);
long firstEnd = startTime + timeStep;
long nextStart = firstEnd + 1;
long endTime = nextStart + timeStep;
insertIntervals(backend, ImmutableList.of(new TmfStateInterval(startTime, startTime + timeStep, intQuark, INT_VAL1),
new TmfStateInterval(startTime, startTime + timeStep, longQuark, LONG_VAL1),
new TmfStateInterval(startTime, startTime + timeStep, doubleQuark, DOUBLE_VAL1),
new TmfStateInterval(startTime, startTime + timeStep, strQuark, STR_VAL1),
new TmfStateInterval(startTime, startTime + timeStep, customQuark, customVal),
new TmfStateInterval(nextStart, endTime, intQuark, INT_VAL2),
new TmfStateInterval(nextStart, endTime, longQuark, LONG_VAL2),
new TmfStateInterval(nextStart, endTime, doubleQuark, DOUBLE_VAL2),
new TmfStateInterval(nextStart, endTime, strQuark, STR_VAL2),
new TmfStateInterval(nextStart, endTime, customQuark, customVal2)));
backend.finishedBuilding(endTime);
/* Make sure the end time corresponds to the backend end time */
assertEquals(endTime, backend.getEndTime());
IStateHistoryBackend backendQuery = getBackendForQuerying(backend);
/* Verify start and end times */
assertEquals("Backend start time", startTime, backendQuery.getStartTime());
assertEquals("Backend end time", endTime, backendQuery.getEndTime());
List<@Nullable ITmfStateInterval> intervals = prepareIntervalList(nbAttribs);
/* Do a full query at start and verify the values */
backendQuery.doQuery(intervals, startTime);
ITmfStateInterval interval = intervals.get(intQuark);
assertNotNull(interval);
assertEquals("Int value after read", INT_VAL1, interval.getValue());
interval = intervals.get(longQuark);
assertNotNull(interval);
assertEquals("Long value after read", LONG_VAL1, interval.getValue());
interval = intervals.get(doubleQuark);
assertNotNull(interval);
assertEquals("Double value after read", DOUBLE_VAL1, interval.getValue());
interval = intervals.get(strQuark);
assertNotNull(interval);
assertEquals("String value after read", STR_VAL1, interval.getValue());
/* Custom */
interval = intervals.get(customQuark);
assertNotNull(interval);
assertEquals("String value after read", customVal, interval.getStateValue());
/* Do a full query at the end and verify the values */
backendQuery.doQuery(intervals, endTime);
interval = intervals.get(intQuark);
assertNotNull(interval);
assertEquals("Int value after read", INT_VAL2, interval.getValue());
interval = intervals.get(longQuark);
assertNotNull(interval);
assertEquals("Long value after read", LONG_VAL2, interval.getValue());
interval = intervals.get(doubleQuark);
assertNotNull(interval);
assertEquals("Double value after read", DOUBLE_VAL2, interval.getValue());
interval = intervals.get(strQuark);
assertNotNull(interval);
assertEquals("String value after read", STR_VAL2, interval.getValue());
/* Custom */
interval = intervals.get(customQuark);
assertNotNull(interval);
assertEquals("String value after read", customVal2, interval.getStateValue());
} catch (TimeRangeException | IOException | StateSystemDisposedException e) {
fail(e.getMessage());
}
}
/**
* Test inserting an interval before the start of the backend
*/
@Test(expected = TimeRangeException.class)
public void testIntervalBeforeStart() {
long startTime = 1000;
try {
IStateHistoryBackend backend = getBackendForBuilding(startTime);
backend.insertPastState(startTime - 1, startTime + 1, 0, INT_VAL1);
} catch (IOException e) {
fail(e.getMessage());
}
}
/**
* Test that negative times are also properly handled.
*
* @throws IOException
* if an IO exception occurred creating the backend.
* @throws StateSystemDisposedException
* if the state system was disposed
* @throws TimeRangeException
* if the time was incorrect.
*/
@Test
public void testNegativeTimes() throws IOException, TimeRangeException, StateSystemDisposedException {
long startTime = -1001;
IStateHistoryBackend backend = getBackendForBuilding(startTime);
for (long t = startTime; t <= 200; t += 10) {
backend.insertPastState(t, t + 10, 0, t);
}
backend.finishedBuilding(210);
IStateHistoryBackend backendQuery = getBackendForQuerying(backend);
ITmfStateInterval poisonInterval = backendQuery.doSingularQuery(-1, 0);
assertNotNull(poisonInterval);
assertEquals(-11L, poisonInterval.getValue());
}
}