ss: Add common unit tests for state history backends
authorPatrick Tasse <patrick.tasse@gmail.com>
Wed, 6 Jan 2016 23:38:17 +0000 (18:38 -0500)
committerPatrick Tasse <patrick.tasse@gmail.com>
Wed, 9 Mar 2016 21:47:23 +0000 (16:47 -0500)
Change-Id: I21b08b85dfbe14f5ff99c5459a4cb10006fbb483
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/64391
Reviewed-by: Hudson CI
Reviewed-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Tested-by: Genevieve Bastien <gbastien+lttng@versatic.net>
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/HistoryTreeBackendTest.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/InMemoryBackendTest.java
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/StateHistoryBackendTestBase.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/ThreadedHistoryTreeBackendTest.java [new file with mode: 0644]

diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/HistoryTreeBackendTest.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/HistoryTreeBackendTest.java
new file mode 100644 (file)
index 0000000..9ab003d
--- /dev/null
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson, EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.backend;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeBackend;
+import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
+import org.junit.After;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test the {@link HistoryTreeBackend} class.
+ *
+ * @author Patrick Tasse
+ * @author Alexandre Montplaisir
+ */
+@RunWith(Parameterized.class)
+public class HistoryTreeBackendTest extends StateHistoryBackendTestBase {
+
+    /** State system ID */
+    protected static final @NonNull String SSID = "test";
+    /** Provider version */
+    protected static final int PROVIDER_VERSION = 0;
+
+    /** Default maximum number of children nodes */
+    protected static final int MAX_CHILDREN = 2;
+    /** Default block size */
+    protected static final int BLOCK_SIZE = 4096;
+
+    /** ReOpen test parameter */
+    protected final boolean fReOpen;
+
+    /** Set of created history tree files */
+    protected Set<File> fHistoryTreeFiles = new HashSet<>();
+    /** Map of backends to history tree file */
+    protected Map<IStateHistoryBackend, File> fBackendMap = new HashMap<>();
+    /** Maximum number of children nodes */
+    protected int fMaxChildren = MAX_CHILDREN;
+    /** Block size */
+    protected int fBlockSize = BLOCK_SIZE;
+
+    /**
+     * @return the test parameters
+     */
+    @Parameters(name = "ReOpen={0}")
+    public static Collection<Boolean> parameters() {
+        return Arrays.asList(Boolean.FALSE, Boolean.TRUE);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param reOpen
+     *            True if the backend should be disposed and re-opened as a new
+     *            backend from the file, or false to use the backend as-is
+     */
+    public HistoryTreeBackendTest(Boolean reOpen) {
+        fReOpen = reOpen;
+    }
+
+    /**
+     * Test cleanup
+     */
+    @After
+    public void teardown() {
+        for (IStateHistoryBackend backend : fBackendMap.keySet()) {
+            backend.dispose();
+        }
+        for (File historyTreeFile : fHistoryTreeFiles) {
+            historyTreeFile.delete();
+        }
+    }
+
+    @Override
+    protected IStateHistoryBackend getBackendForBuilding(long startTime) throws IOException {
+        File historyTreeFile = File.createTempFile("HistoryTreeBackendTest", ".ht");
+        fHistoryTreeFiles.add(historyTreeFile);
+        HistoryTreeBackend backend = new HistoryTreeBackend(SSID, historyTreeFile, PROVIDER_VERSION, startTime, fBlockSize, fMaxChildren);
+        fBackendMap.put(backend, historyTreeFile);
+        return backend;
+    }
+
+    @Override
+    protected IStateHistoryBackend getBackendForQuerying(IStateHistoryBackend backend) throws IOException {
+        if (!fReOpen) {
+            return backend;
+        }
+        File historyTreeFile = fBackendMap.remove(backend);
+        backend.dispose();
+        HistoryTreeBackend reOpenedBackend = new HistoryTreeBackend(SSID, historyTreeFile, PROVIDER_VERSION);
+        fBackendMap.put(reOpenedBackend, historyTreeFile);
+        return reOpenedBackend;
+    }
+}
index 7ac37cde945d77d8f35a20ebcbbcae1b0f1d7def..82cd87e43b9ae1830dcf817c1701614cd942d90a 100644 (file)
 
 package org.eclipse.tracecompass.statesystem.core.tests.backend;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
 import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
@@ -35,8 +40,9 @@ import org.junit.Test;
  *
  * @author Matthew Khouzam
  */
-public class InMemoryBackendTest {
+public class InMemoryBackendTest extends StateHistoryBackendTestBase {
 
+    private static final @NonNull String SSID = "test-ss";
     private static final int NUMBER_OF_ATTRIBUTES = 10;
     private static IStateHistoryBackend fixture;
 
@@ -45,7 +51,7 @@ public class InMemoryBackendTest {
      */
     @BeforeClass
     public static void init() {
-        fixture = StateHistoryBackendFactory.createInMemoryBackend("test-ss", 0);
+        fixture = StateHistoryBackendFactory.createInMemoryBackend(SSID, 0);
         for (int attribute = 0; attribute < NUMBER_OF_ATTRIBUTES; attribute++) {
             for (int timeStart = 0; timeStart < 1000; timeStart++) {
                 try {
@@ -75,6 +81,10 @@ public class InMemoryBackendTest {
         }
     }
 
+    @Override
+    protected IStateHistoryBackend getBackendForBuilding(long startTime) {
+        return StateHistoryBackendFactory.createInMemoryBackend(SSID, startTime);
+    }
 
     /**
      * Test at start time
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/StateHistoryBackendTestBase.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/StateHistoryBackendTestBase.java
new file mode 100644 (file)
index 0000000..83c60c0
--- /dev/null
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson, EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.backend;
+
+import static org.junit.Assert.assertEquals;
+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.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.junit.Test;
+
+/**
+ * Abstract class to test implementations of the {@link IStateHistoryBackend} interface.
+ *
+ * @author Patrick Tasse
+ * @author Alexandre Montplaisir
+ */
+public abstract class StateHistoryBackendTestBase {
+
+    /**
+     * 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 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.getStateValue());
+        }
+    }
+
+    /**
+     * 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);
+
+        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 = new ArrayList<>(nbAttr);
+                for (int i = 0; i < nbAttr; i++) {
+                    stateInfo.add(null);
+                }
+                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 null;
+        }
+        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,
+                    TmfStateValue.newValueLong(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,
+                    TmfStateValue.newValueLong(attr)));
+        }
+
+        buildAndQueryFullRange(startTime, endTime, nbAttr, intervals, false);
+    }
+}
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/ThreadedHistoryTreeBackendTest.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/ThreadedHistoryTreeBackendTest.java
new file mode 100644 (file)
index 0000000..a770800
--- /dev/null
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson, EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.backend;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.ThreadedHistoryTreeBackend;
+import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
+
+/**
+ * Test the {@link ThreadedHistoryTreeBackend} class.
+ *
+ * @author Patrick Tasse
+ * @author Alexandre Montplaisir
+ */
+public class ThreadedHistoryTreeBackendTest extends HistoryTreeBackendTest {
+
+    private static final int QUEUE_SIZE = 10;
+
+    /**
+     * Constructor
+     *
+     * @param reOpen
+     *            True if the backend should be disposed and re-opened as a new
+     *            backend from the file, or false to use the backend as-is
+     */
+    public ThreadedHistoryTreeBackendTest(Boolean reOpen) {
+        super(reOpen);
+    }
+
+    @Override
+    protected IStateHistoryBackend getBackendForBuilding(long startTime) throws IOException {
+        File historyTreeFile = File.createTempFile("ThreadedHistoryTreeBackendTest", ".ht");
+        fHistoryTreeFiles.add(historyTreeFile);
+        ThreadedHistoryTreeBackend backend = new ThreadedHistoryTreeBackend(SSID, historyTreeFile, PROVIDER_VERSION, startTime, QUEUE_SIZE, fBlockSize, fMaxChildren);
+        fBackendMap.put(backend, historyTreeFile);
+        return backend;
+    }
+}
This page took 0.030633 seconds and 5 git commands to generate.