/******************************************************************************* * Copyright (c) 2017 École Polytechnique de Montréal * * 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.internal.provisional.datastore.core.historytree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.PrintStream; import java.nio.channels.ClosedChannelException; import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree; import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HTNode; import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode.NodeType; import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval; import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval; import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader; /** * Some utility classes and methods for the history tree tests * * @author Geneviève Bastien */ public class HtTestUtils { /** * Default block size for the tests */ public static final int BLOCKSIZE = 4096; /** * The factory to read BaseHtObject nodes */ public static final IHTIntervalReader<HTInterval> READ_FACTORY = HTInterval.INTERVAL_READER; /** * Helper method to check the integrity of a tree. For each node of the * tree, it makes sure that elements in nodes are within the node's range * and for core nodes, it makes sure that the children's data is OK. * * @param tree * The history to check */ public static final <E extends IHTInterval, N extends HTNode<E>> void assertTreeIntegrity(AbstractHistoryTree<E, N> tree) { try { for (int i = 0; i < tree.getNodeCount(); i++) { assertNodeIntegrity(tree, tree.getNode(i)); } } catch (ClosedChannelException e) { fail(e.getMessage()); } } private static <E extends IHTInterval, N extends HTNode<E>> void assertNodeIntegrity(AbstractHistoryTree<E, N> tree, N node) { if (node.getNodeType() == NodeType.CORE) { assertChildrenIntegrity(tree, node); } /* Check that all intervals are within the node's range */ for (E object : node.getIntervals()) { assertTrue(String.format("Object start (%d) >= node start (%d)", object.getStart(), node.getNodeStart()), object.getStart() >= node.getNodeStart()); assertTrue(String.format("Object start (%d) <= node end (%d)", object.getStart(), node.getNodeEnd()), object.getStart() <= node.getNodeEnd()); assertTrue(String.format("Object end (%d) >= node start (%d)", object.getEnd(), node.getNodeStart()), object.getEnd() >= node.getNodeStart()); assertTrue(String.format("Object end (%d) <= node end (%d)", object.getEnd(), node.getNodeEnd()), object.getEnd() <= node.getNodeEnd()); } } private static <E extends IHTInterval, N extends HTNode<E>> void assertChildrenIntegrity(AbstractHistoryTree<E, N> tree, N node) { try { /* * Test that this node's start and end times match the start of the * first child and the end of the last child, respectively */ if (node.getNbChildren() > 0) { N childNode = tree.getNode(node.getChild(0)); assertEquals("Equals start time of parent " + node.getSequenceNumber() + " and first child " + childNode.getSequenceNumber(), node.getNodeStart(), childNode.getNodeStart()); if (node.isOnDisk()) { childNode = tree.getNode(node.getLatestChild()); assertEquals("Equals end time of parent " + node.getSequenceNumber() + " and last child " + childNode.getSequenceNumber(), node.getNodeEnd(), childNode.getNodeEnd()); } } /* * Test that children range is within the parent's range */ for (int i = 0; i < node.getNbChildren(); i++) { N childNode = tree.getNode(node.getChild(i)); assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has valid start time", node.getNodeStart() <= childNode.getNodeStart()); if (node.isOnDisk() && childNode.isOnDisk()) { assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has valid end time", childNode.getNodeEnd() <= node.getNodeEnd()); } assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " specific children", tree.verifyChildrenSpecific(node, i, childNode)); assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " intersecting children", tree.verifyIntersectingChildren(node, childNode)); } } catch (ClosedChannelException e) { fail(e.getMessage()); } } /** * Print out the full tree for debugging purposes * * @param writer * PrintWriter in which to write the output * @param tree * The history tree to print * @param printIntervals * Flag to enable full output of the interval information * @param ts * The timestamp that nodes have to intersect for intervals to be * printed. A negative value will print intervals for all nodes. * The timestamp only applies if printIntervals is true. */ public static <E extends IHTInterval, N extends HTNode<E>> void debugPrintFullTree( PrintStream writer, AbstractHistoryTree<E, N> tree, boolean printIntervals, long ts) { preOrderPrint(writer, tree, false, tree.getLatestBranch().get(0), 0, ts); if (printIntervals) { preOrderPrint(writer, tree, true, tree.getLatestBranch().get(0), 0, ts); } writer.println('\n'); } private static <E extends IHTInterval, N extends HTNode<E>> void preOrderPrint( PrintStream writer, AbstractHistoryTree<E, N> tree, boolean printIntervals, N node, int curDepth, long ts) { writer.println(node.toString()); /* * Print intervals only if timestamp is negative or within the range of * the node */ if (printIntervals && (ts <= 0 || (ts >= node.getNodeStart() && ts <= node.getNodeEnd()))) { node.debugPrintIntervals(writer); } switch (node.getNodeType()) { case LEAF: /* Stop if it's the leaf node */ return; case CORE: try { /* Print the child nodes */ for (int i = 0; i < node.getNbChildren(); i++) { N nextNode = tree.readNode(node.getChild(i)); for (int j = 0; j < curDepth; j++) { writer.print(" "); } writer.print("+-"); preOrderPrint(writer, tree, printIntervals, nextNode, curDepth + 1, ts); } } catch (ClosedChannelException e) { throw new RuntimeException(e); } break; default: break; } } }