/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cassandra.index.sasi.utils; import java.util.*; import java.util.concurrent.ThreadLocalRandom; import org.apache.cassandra.index.sasi.disk.Token; import org.apache.cassandra.io.util.FileUtils; import org.junit.Assert; import org.junit.Test; import static org.apache.cassandra.index.sasi.utils.LongIterator.convert; public class RangeUnionIteratorTest { @Test public void testNoOverlappingValues() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 2L, 3L, 5L, 6L })); builder.add(new LongIterator(new long[] { 1L, 7L })); builder.add(new LongIterator(new long[] { 4L, 8L, 9L, 10L })); Assert.assertEquals(convert(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L), convert(builder.build())); } @Test public void testSingleIterator() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 1L, 2L, 4L, 9L })); Assert.assertEquals(convert(1L, 2L, 4L, 9L), convert(builder.build())); } @Test public void testOverlappingValues() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 1L, 4L, 6L, 7L })); builder.add(new LongIterator(new long[] { 2L, 3L, 5L, 6L })); builder.add(new LongIterator(new long[] { 4L, 6L, 8L, 9L, 10L })); List<Long> values = convert(builder.build()); Assert.assertEquals(values.toString(), convert(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L), values); } @Test public void testNoOverlappingRanges() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 1L, 2L, 3L })); builder.add(new LongIterator(new long[] { 4L, 5L, 6L })); builder.add(new LongIterator(new long[] { 7L, 8L, 9L })); Assert.assertEquals(convert(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L), convert(builder.build())); } @Test public void testTwoIteratorsWithSingleValues() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 1L })); builder.add(new LongIterator(new long[] { 1L })); Assert.assertEquals(convert(1L), convert(builder.build())); } @Test public void testDifferentSizeIterators() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 2L, 3L, 5L, 6L, 12L, 13L })); builder.add(new LongIterator(new long[] { 1L, 7L, 14L, 15 })); builder.add(new LongIterator(new long[] { 4L, 5L, 8L, 9L, 10L })); Assert.assertEquals(convert(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 12L, 13L, 14L, 15L), convert(builder.build())); } @Test public void testRandomSequences() { ThreadLocalRandom random = ThreadLocalRandom.current(); long[][] values = new long[random.nextInt(1, 20)][]; int numTests = random.nextInt(10, 20); for (int tests = 0; tests < numTests; tests++) { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); int totalCount = 0; for (int i = 0; i < values.length; i++) { long[] part = new long[random.nextInt(1, 500)]; for (int j = 0; j < part.length; j++) part[j] = random.nextLong(); // all of the parts have to be sorted to mimic SSTable Arrays.sort(part); values[i] = part; builder.add(new LongIterator(part)); totalCount += part.length; } long[] totalOrdering = new long[totalCount]; int index = 0; for (long[] part : values) { for (long value : part) totalOrdering[index++] = value; } Arrays.sort(totalOrdering); int count = 0; RangeIterator<Long, Token> tokens = builder.build(); Assert.assertNotNull(tokens); while (tokens.hasNext()) Assert.assertEquals(totalOrdering[count++], (long) tokens.next().get()); Assert.assertEquals(totalCount, count); } } @Test public void testMinMaxAndCount() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] { 1L, 2L, 3L })); builder.add(new LongIterator(new long[] { 4L, 5L, 6L })); builder.add(new LongIterator(new long[] { 7L, 8L, 9L })); Assert.assertEquals(9L, (long) builder.getMaximum()); Assert.assertEquals(9L, builder.getTokenCount()); RangeIterator<Long, Token> tokens = builder.build(); Assert.assertNotNull(tokens); Assert.assertEquals(1L, (long) tokens.getMinimum()); Assert.assertEquals(9L, (long) tokens.getMaximum()); Assert.assertEquals(9L, tokens.getCount()); for (long i = 1; i < 10; i++) { Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(i, (long) tokens.next().get()); } Assert.assertFalse(tokens.hasNext()); Assert.assertEquals(1L, (long) tokens.getMinimum()); } @Test public void testBuilder() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); Assert.assertNull(builder.getMinimum()); Assert.assertNull(builder.getMaximum()); Assert.assertEquals(0L, builder.getTokenCount()); Assert.assertEquals(0L, builder.rangeCount()); builder.add(new LongIterator(new long[] { 1L, 2L, 3L })); builder.add(new LongIterator(new long[] { 4L, 5L, 6L })); builder.add(new LongIterator(new long[] { 7L, 8L, 9L })); Assert.assertEquals(1L, (long) builder.getMinimum()); Assert.assertEquals(9L, (long) builder.getMaximum()); Assert.assertEquals(9L, builder.getTokenCount()); Assert.assertEquals(3L, builder.rangeCount()); Assert.assertFalse(builder.statistics.isDisjoint()); Assert.assertEquals(1L, (long) builder.ranges.poll().getMinimum()); Assert.assertEquals(4L, (long) builder.ranges.poll().getMinimum()); Assert.assertEquals(7L, (long) builder.ranges.poll().getMinimum()); RangeIterator<Long, Token> tokens = RangeUnionIterator.build(new ArrayList<RangeIterator<Long, Token>>() {{ add(new LongIterator(new long[]{1L, 2L, 4L})); add(new LongIterator(new long[]{3L, 5L, 6L})); }}); Assert.assertEquals(convert(1L, 2L, 3L, 4L, 5L, 6L), convert(tokens)); FileUtils.closeQuietly(tokens); RangeIterator emptyTokens = RangeUnionIterator.builder().build(); Assert.assertEquals(0, emptyTokens.getCount()); builder = RangeUnionIterator.builder(); Assert.assertEquals(0L, builder.add((RangeIterator<Long, Token>) null).rangeCount()); Assert.assertEquals(0L, builder.add((List<RangeIterator<Long, Token>>) null).getTokenCount()); Assert.assertEquals(0L, builder.add(new LongIterator(new long[] {})).rangeCount()); RangeIterator<Long, Token> single = new LongIterator(new long[] { 1L, 2L, 3L }); RangeIterator<Long, Token> range = RangeIntersectionIterator.<Long, Token>builder().add(single).build(); // because build should return first element if it's only one instead of building yet another iterator Assert.assertEquals(range, single); } @Test public void testSkipTo() { RangeUnionIterator.Builder<Long, Token> builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[]{1L, 2L, 3L})); builder.add(new LongIterator(new long[]{4L, 5L, 6L})); builder.add(new LongIterator(new long[]{7L, 8L, 9L})); RangeIterator<Long, Token> tokens = builder.build(); Assert.assertNotNull(tokens); tokens.skipTo(5L); Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(5L, (long) tokens.next().get()); tokens.skipTo(7L); Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(7L, (long) tokens.next().get()); tokens.skipTo(10L); Assert.assertFalse(tokens.hasNext()); Assert.assertEquals(1L, (long) tokens.getMinimum()); Assert.assertEquals(9L, (long) tokens.getMaximum()); } @Test public void testMergingMultipleIterators() { RangeUnionIterator.Builder<Long, Token> builderA = RangeUnionIterator.builder(); builderA.add(new LongIterator(new long[] { 1L, 3L, 5L })); builderA.add(new LongIterator(new long[] { 8L, 10L, 12L })); RangeUnionIterator.Builder<Long, Token> builderB = RangeUnionIterator.builder(); builderB.add(new LongIterator(new long[] { 7L, 9L, 11L })); builderB.add(new LongIterator(new long[] { 2L, 4L, 6L })); RangeIterator<Long, Token> union = RangeUnionIterator.build(Arrays.asList(builderA.build(), builderB.build())); Assert.assertEquals(convert(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L), convert(union)); } @Test public void testRangeIterator() { LongIterator tokens = new LongIterator(new long[] { 0L, 1L, 2L, 3L }); Assert.assertEquals(0L, (long) tokens.getMinimum()); Assert.assertEquals(3L, (long) tokens.getMaximum()); for (int i = 0; i <= 3; i++) { Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(i, (long) tokens.getCurrent()); Assert.assertEquals(i, (long) tokens.next().get()); } tokens = new LongIterator(new long[] { 0L, 1L, 3L, 5L }); Assert.assertEquals(3L, (long) tokens.skipTo(2L).get()); Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(3L, (long) tokens.getCurrent()); Assert.assertEquals(3L, (long) tokens.next().get()); Assert.assertEquals(5L, (long) tokens.skipTo(5L).get()); Assert.assertTrue(tokens.hasNext()); Assert.assertEquals(5L, (long) tokens.getCurrent()); Assert.assertEquals(5L, (long) tokens.next().get()); LongIterator empty = new LongIterator(new long[0]); Assert.assertNull(empty.skipTo(3L)); Assert.assertFalse(empty.hasNext()); } @Test public void emptyRangeTest() { RangeIterator.Builder<Long, Token> builder; RangeIterator<Long, Token> range; // empty, then non-empty builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] {})); for (int i = 0; i < 10; i++) builder.add(new LongIterator(new long[] {i + 10})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(19), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(10, range.getCount()); builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] {})); builder.add(new LongIterator(new long[] {10})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(10), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(1, range.getCount()); // non-empty, then empty builder = RangeUnionIterator.builder(); for (int i = 0; i < 10; i++) builder.add(new LongIterator(new long[] {i + 10})); builder.add(new LongIterator(new long[] {})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(19), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(10, range.getCount()); builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] {10})); builder.add(new LongIterator(new long[] {})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(10), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(1, range.getCount()); // empty, then non-empty then empty again builder = RangeUnionIterator.builder(); builder.add(new LongIterator(new long[] {})); for (int i = 0; i < 10; i++) builder.add(new LongIterator(new long[] {i + 10})); builder.add(new LongIterator(new long[] {})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(19), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(10, range.getCount()); // non-empty, empty, then non-empty again builder = RangeUnionIterator.builder(); for (int i = 0; i < 5; i++) builder.add(new LongIterator(new long[] {i + 10})); builder.add(new LongIterator(new long[] {})); for (int i = 5; i < 10; i++) builder.add(new LongIterator(new long[] {i + 10})); range = builder.build(); Assert.assertEquals(Long.valueOf(10), range.getMinimum()); Assert.assertEquals(Long.valueOf(19), range.getMaximum()); Assert.assertTrue(range.hasNext()); Assert.assertEquals(10, range.getCount()); } }