| /******************************************************************************* |
| * Copyright (c) 2016 Ericsson |
| * |
| * 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; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.tracecompass.common.core.NonNullUtils; |
| import org.eclipse.tracecompass.datastore.core.interval.IHTIntervalReader; |
| import org.eclipse.tracecompass.datastore.core.serialization.ISafeByteBufferWriter; |
| import org.eclipse.tracecompass.datastore.core.serialization.SafeByteBufferFactory; |
| import org.eclipse.tracecompass.segmentstore.core.BasicSegment; |
| import org.eclipse.tracecompass.segmentstore.core.ISegment; |
| import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; |
| import org.eclipse.tracecompass.segmentstore.core.SegmentComparators; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| |
| /** |
| * Unit tests for intersecting elements in an SegmentStore |
| * |
| * Originally the TreeMapStoreTest, copied for this internal implementation. The |
| * test was barely changed as it tests the interface and not the internals. |
| * |
| * @author Matthew Khouzam |
| */ |
| public abstract class AbstractTestSegmentStore { |
| |
| /** |
| * The segment store |
| */ |
| protected final ISegmentStore<@NonNull TestSegment> fSegmentStore; |
| |
| /** |
| * Get the segment store to test |
| * |
| * @return the segment store |
| */ |
| protected abstract ISegmentStore<@NonNull TestSegment> getSegmentStore(); |
| |
| /** |
| * Get the segment store to test with initial data |
| * |
| * @param data |
| * the data |
| * |
| * @return the segment store |
| */ |
| protected abstract ISegmentStore<@NonNull TestSegment> getSegmentStore(@NonNull TestSegment @NonNull [] data); |
| |
| private static final @NonNull TestSegment SEGMENT_2_6 = new TestSegment(2, 6, "test"); |
| private static final @NonNull TestSegment SEGMENT_4_6 = new TestSegment(4, 6, "test2"); |
| private static final @NonNull TestSegment SEGMENT_4_8 = new TestSegment(4, 8, "test3"); |
| private static final @NonNull TestSegment SEGMENT_6_8 = new TestSegment(6, 8, "test"); |
| private static final @NonNull TestSegment SEGMENT_10_14 = new TestSegment(10, 14, "test"); |
| /** |
| * A sample segment list |
| */ |
| protected static final List<@NonNull TestSegment> SEGMENTS = ImmutableList.of(SEGMENT_2_6, SEGMENT_4_6, SEGMENT_4_8, SEGMENT_6_8, SEGMENT_10_14); |
| private static final List<@NonNull TestSegment> REVERSE_SEGMENTS = Lists.reverse(SEGMENTS); |
| |
| /** |
| * A test type of segment that can be serialized with the safe buffers. It |
| * has an extra payload |
| * |
| * @author Geneviève Bastien |
| */ |
| protected static final class TestSegment implements ISegment { |
| |
| /** |
| * The reader for this class |
| */ |
| public static final @NonNull IHTIntervalReader<@NonNull TestSegment> DESERIALISER = buffer -> new TestSegment(buffer.getLong(), buffer.getLong(), buffer.getString()); |
| |
| /** |
| * |
| */ |
| private static final long serialVersionUID = -2242452053089575887L; |
| |
| private final long fStart; |
| private final long fEnd; |
| private final @NonNull String fPayload; |
| |
| /** |
| * Constructor |
| * |
| * @param start |
| * Start of this segment |
| * @param end |
| * End of this segment |
| * @param payload |
| * Payload |
| */ |
| public TestSegment(long start, long end, @NonNull String payload) { |
| fStart = start; |
| fEnd = end; |
| fPayload = payload; |
| } |
| |
| @Override |
| public long getStart() { |
| return fStart; |
| } |
| |
| @Override |
| public long getEnd() { |
| return fEnd; |
| } |
| |
| /** |
| * Get the payload of this segment |
| * |
| * @return The payload |
| */ |
| public String getPayload() { |
| return fPayload; |
| } |
| |
| @Override |
| public int getSizeOnDisk() { |
| return 2 * Long.BYTES + SafeByteBufferFactory.getStringSizeInBuffer(fPayload); |
| } |
| |
| @Override |
| public void writeSegment(@NonNull ISafeByteBufferWriter buffer) { |
| buffer.putLong(fStart); |
| buffer.putLong(fEnd); |
| buffer.putString(fPayload); |
| } |
| |
| } |
| |
| /** |
| * Constructor |
| */ |
| public AbstractTestSegmentStore() { |
| super(); |
| fSegmentStore = getSegmentStore(); |
| } |
| |
| /** |
| * Asserts that 2 segments are equal. Some backend may not return exactly |
| * the same type of segment so the main assert will be false, but the |
| * segments are in fact identical |
| * |
| * @param expected |
| * The expected segment |
| * @param actual |
| * The actual segment |
| */ |
| protected void assertSegmentsEqual(ISegment expected, ISegment actual) { |
| assertEquals(expected, actual); |
| } |
| |
| /** |
| * Initialize data (test vector) that will be tested |
| */ |
| @Before |
| public void setup() { |
| for (TestSegment segment : SEGMENTS) { |
| fSegmentStore.add(segment); |
| } |
| } |
| |
| /** |
| * Dispose of the segment store |
| */ |
| @After |
| public void teardown() { |
| fSegmentStore.dispose(); |
| } |
| |
| /** |
| * Testing method size() |
| */ |
| @Test |
| public void testSize() { |
| assertEquals(SEGMENTS.size(), fSegmentStore.size()); |
| } |
| |
| /** |
| * Testing isEmpty() method |
| */ |
| @Test |
| public void testIsEmpty() { |
| assertFalse(fSegmentStore.isEmpty()); |
| fSegmentStore.clear(); |
| assertTrue(fSegmentStore.isEmpty()); |
| } |
| |
| /** |
| * Testing adding a collection with the addAll method |
| */ |
| @Test |
| public void testAddAll() { |
| assertFalse(fSegmentStore.isEmpty()); |
| fSegmentStore.clear(); |
| assertTrue(fSegmentStore.isEmpty()); |
| fSegmentStore.addAll(SEGMENTS); |
| assertTrue(fSegmentStore.containsAll(SEGMENTS)); |
| } |
| |
| /** |
| * Testing "copy" constructor |
| */ |
| @Test |
| public void testAddAllConstructor() { |
| ISegmentStore<@NonNull TestSegment> other = getSegmentStore(fSegmentStore.toArray(new TestSegment[fSegmentStore.size()])); |
| assertTrue(fSegmentStore.containsAll(other)); |
| assertTrue(other.containsAll(fSegmentStore)); |
| } |
| |
| /** |
| * Testing "copy" constructor out of order |
| */ |
| @Test |
| public void testAddAllConstructorOutOfOrder() { |
| ISegmentStore<@NonNull TestSegment> other = getSegmentStore(REVERSE_SEGMENTS.toArray(new TestSegment[fSegmentStore.size()])); |
| assertTrue(fSegmentStore.containsAll(other)); |
| assertTrue(other.containsAll(fSegmentStore)); |
| } |
| |
| /** |
| * Testing adding an out of order collection with the addAll method |
| */ |
| @Test |
| public void testAddAllOutOfOrder() { |
| assertFalse(fSegmentStore.isEmpty()); |
| fSegmentStore.clear(); |
| assertTrue(fSegmentStore.isEmpty()); |
| fSegmentStore.addAll(REVERSE_SEGMENTS); |
| assertTrue(fSegmentStore.containsAll(SEGMENTS)); |
| } |
| |
| /** |
| * Test the contains() method. |
| */ |
| @Test |
| public void testContains() { |
| ISegment otherSegment = new BasicSegment(0, 20); |
| |
| assertTrue(fSegmentStore.contains(SEGMENT_2_6)); |
| assertTrue(fSegmentStore.contains(SEGMENT_4_8)); |
| assertFalse(fSegmentStore.contains(otherSegment)); |
| } |
| |
| /** |
| * Test containsAll() method |
| */ |
| public void testContainsAll() { |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| |
| store.add(SEGMENT_2_6); |
| assertTrue(store.containsAll(Collections.emptyList())); |
| assertTrue(store.containsAll(Collections.singleton(SEGMENT_2_6))); |
| assertFalse(store.containsAll(Collections.singleton(SEGMENT_4_6))); |
| } |
| |
| /** |
| * Test the toArray() method. |
| */ |
| @Test |
| public void testToObjectArray() { |
| Object[] array = fSegmentStore.toArray(); |
| |
| assertEquals(SEGMENTS.size(), array.length); |
| assertTrue(Arrays.asList(array).containsAll(SEGMENTS)); |
| } |
| |
| /** |
| * Test the toArray(T[]) method. |
| */ |
| @Test |
| public void testToSpecificArray() { |
| ISegment[] array = fSegmentStore.toArray(new ISegment[0]); |
| |
| assertEquals(SEGMENTS.size(), array.length); |
| assertTrue(Arrays.asList(array).containsAll(SEGMENTS)); |
| } |
| |
| /** |
| * Test the toArray(T[]) method with a subtype of ISegment. |
| */ |
| @Test |
| public void testToSpecifyArraySubtype() { |
| ISegmentStore<@NonNull TestSegment> tms2 = getSegmentStore(); |
| TestSegment otherSegment = new TestSegment(2, 6, "test"); |
| tms2.add(otherSegment); |
| TestSegment[] array = tms2.toArray(new TestSegment[0]); |
| |
| assertEquals(1, array.length); |
| assertTrue(Arrays.asList(array).contains(otherSegment)); |
| |
| tms2.dispose(); |
| } |
| |
| /** |
| * Test the iteration order of the complete segment store. |
| */ |
| @Test |
| public void testIterationOrder() { |
| int i = 0; |
| for (ISegment segment : fSegmentStore) { |
| assertSegmentsEqual(SEGMENTS.get(i++), segment); |
| } |
| } |
| |
| /** |
| * Test the iteration order when the elements are not inserted in sorted |
| * order. |
| */ |
| @Test |
| public void testIterationOrderNonSortedInsertion() { |
| /* Prepare the segment store, we don't use the 'fixture' in this test */ |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| for (TestSegment segment : REVERSE_SEGMENTS) { |
| store.add(checkNotNull(segment)); |
| } |
| |
| /* |
| * Test each element one by one, the iteration order should follow the |
| * start times, not the insertion order. |
| */ |
| int i = 0; |
| for (TestSegment segment : store) { |
| assertSegmentsEqual(SEGMENTS.get(i++), segment); |
| } |
| |
| /* Manually dispose our own store */ |
| store.dispose(); |
| } |
| |
| /** |
| * Testing method |
| * {@link ISegmentStore#getIntersectingElements(long start, long end)} |
| */ |
| @Test |
| public void testGetIntersectingElementsRange() { |
| |
| Iterable<TestSegment> intersectingElements; |
| |
| /* |
| * Range that does not include any segment |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(16, 20); |
| assertEquals(0, Iterables.size(intersectingElements)); |
| |
| /* |
| * Range start time : Before first segment start time Range end time : |
| * After last segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(1, 15); |
| assertEquals(5, Iterables.size(intersectingElements)); |
| |
| /* |
| * Range start time : On first segment start time Range end time : On |
| * last segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(2, 14); |
| assertEquals(5, Iterables.size(intersectingElements)); |
| |
| /* |
| * Range start time : After one segment start time Range end time : |
| * Before one segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(11, 13); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_10_14, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Range start time : On one segment start time Range end time : On one |
| * segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(10, 14); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_10_14, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Range start time : On last segment end time Range end time : After |
| * last segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(14, 18); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_10_14, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Range start time : Before first segment start time Range end time : |
| * On first segment start time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(1, 2); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_2_6, Iterables.getOnlyElement(intersectingElements)); |
| } |
| |
| /** |
| * Testing method {@link ISegmentStore#getIntersectingElements(long time)} |
| */ |
| @Test |
| public void testGetIntersectingElementsTime() { |
| |
| Iterable<TestSegment> intersectingElements; |
| |
| /* |
| * Time between segment start time and end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(3); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_2_6, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Time on segment start time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(2); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_2_6, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Time on segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(14); |
| assertEquals(1, Iterables.size(intersectingElements)); |
| assertSegmentsEqual(SEGMENT_10_14, Iterables.getOnlyElement(intersectingElements)); |
| |
| /* |
| * Time overlapping many segments |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(6); |
| assertEquals(4, Iterables.size(intersectingElements)); |
| |
| /* |
| * Time between segments |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(9); |
| assertEquals(0, Iterables.size(intersectingElements)); |
| |
| /* |
| * Time before all segment start time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(1); |
| assertEquals(0, Iterables.size(intersectingElements)); |
| |
| /* |
| * Time after all segment end time |
| */ |
| intersectingElements = fSegmentStore.getIntersectingElements(15); |
| assertEquals(0, Iterables.size(intersectingElements)); |
| } |
| |
| /** |
| * Testing method {@link ISegmentStore#dispose()} |
| */ |
| @Test |
| public void testDispose() { |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| store.add(SEGMENT_2_6); |
| store.dispose(); |
| assertEquals(0, store.size()); |
| } |
| |
| /** |
| * Test iterating over a store being built. |
| * |
| * bug 500607 |
| */ |
| @Test |
| public void testIterator() { |
| Collection<@NonNull TestSegment> beforeExpected = ImmutableList.of(SEGMENT_2_6); |
| Collection<@NonNull TestSegment> afterExpected = ImmutableList.of(SEGMENT_2_6, SEGMENT_4_8); |
| Collection<@NonNull TestSegment> lastExpected = ImmutableList.of(SEGMENT_2_6, SEGMENT_4_8, SEGMENT_6_8); |
| Collection<@NonNull TestSegment> fixture = new ArrayList<>(); |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| |
| // Add one segment to the segment store and iterate |
| store.add(SEGMENT_2_6); |
| for (TestSegment item : store) { |
| fixture.add(item); |
| } |
| assertEquals(beforeExpected, fixture); |
| |
| // Add a second segment to the store and iterate |
| fixture.clear(); |
| store.add(SEGMENT_4_8); |
| for (TestSegment item : store) { |
| fixture.add(item); |
| } |
| assertEquals(afterExpected, fixture); |
| |
| fixture.clear(); |
| // Take an iterator |
| Iterator<@NonNull TestSegment> iter = store.iterator(); |
| |
| // Add a third segment to the store and iterate |
| store.add(SEGMENT_6_8); |
| Iterator<@NonNull TestSegment> iter2 = store.iterator(); |
| fixture.clear(); |
| |
| // Make sure the first iterator take has at least 2 elements (depends on |
| // implementation) and the second has 3 elements |
| while (iter.hasNext()) { |
| fixture.add(iter.next()); |
| } |
| assertTrue(fixture.size() >= 2); |
| fixture.clear(); |
| while (iter2.hasNext()) { |
| fixture.add(iter2.next()); |
| } |
| assertEquals(lastExpected, fixture); |
| } |
| |
| /** |
| * Test to check ordered iterators |
| */ |
| @Test |
| public void testSortedIterator() { |
| List<@NonNull Comparator<ISegment>> comparators = new LinkedList<>(); |
| comparators.add(SegmentComparators.INTERVAL_END_COMPARATOR); |
| comparators.add(NonNullUtils.checkNotNull(SegmentComparators.INTERVAL_END_COMPARATOR.reversed())); |
| comparators.add(SegmentComparators.INTERVAL_START_COMPARATOR); |
| comparators.add(NonNullUtils.checkNotNull(SegmentComparators.INTERVAL_START_COMPARATOR.reversed())); |
| comparators.add(SegmentComparators.INTERVAL_LENGTH_COMPARATOR); |
| comparators.add(NonNullUtils.checkNotNull(SegmentComparators.INTERVAL_LENGTH_COMPARATOR.reversed())); |
| |
| Iterable<TestSegment> iterable; |
| for (Comparator<ISegment> comparator : comparators) { |
| iterable = fSegmentStore.iterator(comparator); |
| verifySortedIterable(iterable, 5, comparator); |
| iterable = fSegmentStore.getIntersectingElements(5, comparator); |
| verifySortedIterable(iterable, 3, comparator); |
| iterable = fSegmentStore.getIntersectingElements(7, 14, comparator); |
| verifySortedIterable(iterable, 3, comparator); |
| } |
| } |
| |
| private static void verifySortedIterable(Iterable<TestSegment> iterable, int expectedSize, Comparator<ISegment> comparator) { |
| // check its size |
| assertEquals(expectedSize, Iterables.size(iterable)); |
| Iterator<TestSegment> iterator = iterable.iterator(); |
| // check the order |
| ISegment prev, current = iterator.next(); |
| while (iterator.hasNext()) { |
| prev = current; |
| current = iterator.next(); |
| assertTrue(comparator.compare(prev, current) <= 0); |
| } |
| } |
| |
| /** |
| * Test retainAll() contract |
| */ |
| @Test(expected = UnsupportedOperationException.class) |
| public void testRetainAll() { |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| |
| store.add(SEGMENT_2_6); |
| store.retainAll(Collections.emptyList()); |
| } |
| |
| /** |
| * Test remove() contract |
| */ |
| @Test(expected = UnsupportedOperationException.class) |
| public void testRemove() { |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| |
| store.add(SEGMENT_2_6); |
| store.remove(SEGMENT_2_6); |
| } |
| |
| /** |
| * Test removeAll() contract |
| */ |
| @Test(expected = UnsupportedOperationException.class) |
| public void testRemoveAll() { |
| ISegmentStore<@NonNull TestSegment> store = getSegmentStore(); |
| |
| store.add(SEGMENT_2_6); |
| store.removeAll(Collections.emptyList()); |
| } |
| } |