| /******************************************************************************* |
| * Copyright (c) 2016 É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 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.segmentstore.core.tests.htStore; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.tracecompass.common.core.NonNullUtils; |
| import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree; |
| import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.AbstractOverlappingHistoryTreeTestBase; |
| import org.eclipse.tracecompass.internal.segmentstore.core.segmentHistoryTree.SegmentHistoryTree; |
| import org.eclipse.tracecompass.internal.segmentstore.core.segmentHistoryTree.SegmentTreeNode; |
| import org.eclipse.tracecompass.segmentstore.core.BasicSegment; |
| import org.eclipse.tracecompass.segmentstore.core.tests.historytree.SegmentHistoryTreeStub; |
| import org.junit.Test; |
| |
| import com.google.common.collect.Iterators; |
| |
| /** |
| * Test the segment history tree itself, using a stub history tree with stub |
| * nodes |
| * |
| * @author Geneviève Bastien |
| */ |
| @NonNullByDefault |
| public class SegmentHistoryTreeTest extends AbstractOverlappingHistoryTreeTestBase<BasicSegment, SegmentTreeNode<BasicSegment>> { |
| |
| private static final BasicSegment DEFAULT_OBJECT = new BasicSegment(0, 0); |
| |
| @Override |
| protected SegmentHistoryTreeStub<BasicSegment> createHistoryTree(@NonNull File stateHistoryFile, int blockSize, int maxChildren, int providerVersion, long treeStart) |
| throws IOException { |
| return new SegmentHistoryTreeStub<>(stateHistoryFile, |
| blockSize, |
| maxChildren, |
| providerVersion, |
| treeStart, |
| BasicSegment.BASIC_SEGMENT_READ_FACTORY); |
| } |
| |
| @Override |
| protected SegmentHistoryTreeStub<BasicSegment> createHistoryTree(@NonNull File existingStateFile, int expectedProviderVersion) throws IOException { |
| return new SegmentHistoryTreeStub<>(existingStateFile, expectedProviderVersion, |
| BasicSegment.BASIC_SEGMENT_READ_FACTORY); |
| } |
| |
| @Override |
| protected BasicSegment createInterval(long start, long end) { |
| return new BasicSegment(start, end); |
| } |
| |
| @Override |
| protected long fillValues(@NonNull AbstractHistoryTree<@NonNull BasicSegment, SegmentTreeNode<@NonNull BasicSegment>> ht, int fillSize, long start) { |
| int nbValues = fillSize / DEFAULT_OBJECT.getSizeOnDisk(); |
| for (int i = 0; i < nbValues; i++) { |
| ht.insert(createInterval(start + i, start + i + 1)); |
| } |
| return start + nbValues; |
| } |
| |
| /** |
| * Test the {@link SegmentHistoryTree#isEmpty()} method |
| */ |
| @Test |
| public void testIsEmpty() { |
| long start = 10L; |
| SegmentHistoryTreeStub<BasicSegment> oht = (SegmentHistoryTreeStub<BasicSegment>) setupSmallTree(3, start); |
| |
| assertTrue(oht.isEmpty()); |
| |
| // Add one element, the tree should not be empty anymore |
| oht.insert(createInterval(start, start + 1)); |
| assertFalse(oht.isEmpty()); |
| |
| /* Fill a first node */ |
| SegmentTreeNode<BasicSegment> node = oht.getLatestLeaf(); |
| long time = fillValues(oht, node.getNodeFreeSpace(), start); |
| |
| /* |
| * Add an element that will create a sibling and a root node, the root |
| * node should be empty but not the tree |
| */ |
| oht.insert(createInterval(time, time + 1)); |
| assertFalse(oht.isEmpty()); |
| } |
| |
| /** |
| * Test the {@link SegmentHistoryTree#size()} method |
| * |
| * @throws IOException |
| * Exceptions during the test |
| */ |
| @Test |
| public void testSizeAndIterator() throws IOException { |
| long start = 10L; |
| SegmentHistoryTreeStub<BasicSegment> oht = (SegmentHistoryTreeStub<BasicSegment>) setupSmallTree(3, start); |
| |
| int nbSegments = 0; |
| assertEquals(nbSegments, oht.size()); |
| |
| // Add one element, the tree have one element |
| oht.insert(createInterval(start, start + 1)); |
| nbSegments++; |
| assertEquals(nbSegments, oht.size()); |
| |
| /* Fill a first node */ |
| SegmentTreeNode<BasicSegment> node = oht.getLatestLeaf(); |
| nbSegments += node.getNodeFreeSpace() / DEFAULT_OBJECT.getSizeOnDisk(); |
| long time = fillValues(oht, node.getNodeFreeSpace(), start); |
| assertEquals(nbSegments, oht.size()); |
| |
| /* |
| * Add an element that will create a sibling and a root node, the root |
| * node should be empty but not the tree |
| */ |
| oht.insert(createInterval(time, time + 1)); |
| nbSegments++; |
| // Add some intervals that will go in the top node |
| oht.insert(createInterval(start, time)); |
| oht.insert(createInterval(start, start + 10)); |
| nbSegments += 2; |
| assertEquals(nbSegments, oht.size()); |
| |
| /* |
| * Fill the latest node |
| */ |
| node = oht.getLatestLeaf(); |
| nbSegments += node.getNodeFreeSpace() / DEFAULT_OBJECT.getSizeOnDisk(); |
| time = fillValues(oht, node.getNodeFreeSpace(), start); |
| assertEquals(nbSegments, oht.size()); |
| |
| // Close the tree |
| oht.closeTree(oht.getTreeEnd()); |
| |
| // Create a reader history tree |
| oht = (SegmentHistoryTreeStub<BasicSegment>) createHistoryTreeReader(); |
| assertEquals(nbSegments, oht.size()); |
| |
| // Test the iterator on that tree |
| Iterator<BasicSegment> iterator = oht.iterator(); |
| assertNotNull(iterator); |
| int count = Iterators.size(iterator); |
| assertEquals(nbSegments, count); |
| |
| // Test the intersecting elements |
| Iterable<BasicSegment> intersectingElements = oht.getIntersectingElements(start, time); |
| count = 0; |
| // While doing this iterator, count the number of segments of a smaller |
| // time range, it will be needed later |
| long rangeStart = (long) (start + (time - start) * 0.4); |
| long rangeEnd = (long) (time - (time - start) * 0.4); |
| int nbInRange = 0; |
| for (BasicSegment segment : intersectingElements) { |
| count++; |
| if (segment.getStart() <= rangeEnd && rangeStart <= segment.getEnd()) { |
| nbInRange++; |
| } |
| } |
| assertEquals(nbSegments, count); |
| |
| // Test intersecting elements for a time range |
| count = Iterators.size(oht.getIntersectingElements(rangeStart, rangeEnd).iterator()); |
| assertEquals(nbInRange, count); |
| |
| // Test intersecting elements with start time ordering |
| Comparator<BasicSegment> cmp = NonNullUtils.checkNotNull(Comparator.comparing(BasicSegment::getStart)); |
| assertSortedIteration(oht, rangeStart, rangeEnd, cmp, nbInRange); |
| |
| // Test intersecting elements with end time ordering |
| cmp = NonNullUtils.checkNotNull(Comparator.comparing(BasicSegment::getEnd)); |
| assertSortedIteration(oht, rangeStart, rangeEnd, cmp, nbInRange); |
| |
| // Test intersecting elements with duration ordering |
| cmp = NonNullUtils.checkNotNull(Comparator.comparing(BasicSegment::getLength)); |
| assertSortedIteration(oht, rangeStart, rangeEnd, cmp, nbInRange); |
| } |
| |
| private static void assertSortedIteration(SegmentHistoryTreeStub<@NonNull BasicSegment> oht, long rangeStart, long rangeEnd, Comparator<@NonNull BasicSegment> cmp, int nbInRange) { |
| int count = 0; |
| BasicSegment prev = DEFAULT_OBJECT; |
| for (BasicSegment segment : oht.getIntersectingElements(rangeStart, rangeEnd, cmp)) { |
| count++; |
| assertTrue("Segment comparison at " + count, cmp.compare(prev, segment) <= 0); |
| } |
| assertEquals(nbInRange, count); |
| } |
| |
| } |