/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.github.geophile.erdo.map.mergescan;
import com.github.geophile.erdo.map.MapBehaviorTestBase;
import com.github.geophile.erdo.AbstractKey;
import com.github.geophile.erdo.map.LazyRecord;
import com.github.geophile.erdo.map.MapCursor;
import com.github.geophile.erdo.map.SealedMap;
import com.github.geophile.erdo.map.privatemap.PrivateMap;
import com.github.geophile.erdo.transaction.DeadlockException;
import com.github.geophile.erdo.transaction.TransactionRolledBackException;
import org.junit.Test;
import java.io.IOException;
import java.util.*;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MergeCursorTest extends MapBehaviorTestBase
{
@Test
public void testNoInputs() throws IOException, InterruptedException
{
MergeCursor cursor = forwardMergeCursor();
assertNull(cursor.next());
}
@Test
public void testOneInput()
throws IOException,
InterruptedException,
DeadlockException,
TransactionRolledBackException
{
for (int n = 0; n <= MAX_N; n++) {
PrivateMap map = new PrivateMap(FACTORY);
// load
{
for (int i = 0; i < n; i++) {
map.put(newRecord(i, null), false);
}
}
// forward
{
MergeCursor cursor = forwardMergeCursor(map);
int expected = 0;
LazyRecord record;
while ((record = cursor.next()) != null) {
assertEquals(expected++, key(record));
}
assertEquals(n, expected);
}
// backward
{
MergeCursor cursor = backwardMergeCursor(map);
int expected = n;
LazyRecord record;
while ((record = cursor.previous()) != null) {
assertEquals(--expected, key(record));
}
assertEquals(0, expected);
}
}
}
@Test
public void testTwoInputs()
throws IOException,
InterruptedException,
DeadlockException,
TransactionRolledBackException
{
PrivateMap openMap;
for (int nEven = 0; nEven <= MAX_N; nEven++) {
openMap = new PrivateMap(FACTORY);
List<Integer> expectedEven = new ArrayList<>();
for (int i = 0; i < nEven; i++) {
int even = 2 * i;
openMap.put(newRecord(even, null), false);
expectedEven.add(even);
}
SealedMap evenMap = openMap;
for (int nOdd = 0; nOdd <= MAX_N; nOdd++) {
openMap = new PrivateMap(FACTORY);
List<Integer> expected = new ArrayList<>(expectedEven);
// Odd numbers
for (int i = 0; i < nEven; i++) {
int odd = 2 * i + 1;
openMap.put(newRecord(odd, null), false);
expected.add(odd);
}
SealedMap oddMap = openMap;
// forward
{
Collections.sort(expected);
Iterator<Integer> expectedIterator = expected.iterator();
MergeCursor cursor = forwardMergeCursor(evenMap, oddMap);
LazyRecord lazyRecord;
while ((lazyRecord = cursor.next()) != null) {
assertEquals(expectedIterator.next().intValue(), key(lazyRecord));
}
assertTrue(!expectedIterator.hasNext());
}
// backward
{
Collections.reverse(expected);
Iterator<Integer> expectedIterator = expected.iterator();
MergeCursor cursor = backwardMergeCursor(evenMap, oddMap);
LazyRecord lazyRecord;
while ((lazyRecord = cursor.previous()) != null) {
assertEquals(expectedIterator.next().intValue(), key(lazyRecord));
}
assertTrue(!expectedIterator.hasNext());
}
}
}
}
@Test
public void testManyInputs() throws IOException, InterruptedException
{
final int TRIALS = 1000;
final int MAX_INPUTS = 20;
final int AVE_RECORDS_PER_INPUT = 2;
Random random = new Random(123456789);
for (int t = 0; t < TRIALS; t++) {
// load
int nInputs = 1 + random.nextInt(MAX_INPUTS);
PrivateMap[] maps = new PrivateMap[nInputs];
for (int i = 0; i < nInputs; i++) {
maps[i] = new PrivateMap(FACTORY);
}
int nRecords = nInputs * AVE_RECORDS_PER_INPUT;
for (int k = 0; k < nRecords; k++) {
int m = random.nextInt(nInputs);
maps[m].put(newRecord(k, null), false);
}
for (int i = 0; i < nInputs; i++) {
MapCursor cursor = maps[i].cursor(null, false);
LazyRecord lazyRecord;
StringBuffer buffer = new StringBuffer();
while ((lazyRecord = cursor.next()) != null) {
buffer.append(" ");
buffer.append(lazyRecord.materializeRecord().key());
}
}
// forward
{
MergeCursor cursor = new MergeCursor(null, true);
for (PrivateMap map : maps) {
cursor.addInput(map.cursor(null, false));
}
cursor.start();
LazyRecord lazyRecord;
int expected = 0;
while ((lazyRecord = cursor.next()) != null) {
assertEquals(expected++, key(lazyRecord));
}
assertEquals(nRecords, expected);
}
// backward
{
MergeCursor cursor = new MergeCursor(null, false);
for (PrivateMap map : maps) {
cursor.addInput(map.cursor(null, false));
}
cursor.start();
LazyRecord lazyRecord;
int expected = nRecords;
while ((lazyRecord = cursor.previous()) != null) {
assertEquals(--expected, key(lazyRecord));
}
assertEquals(0, expected);
}
// changing direction (two steps forward, one step back)
{
MergeCursor cursor = new MergeCursor(null, true);
for (PrivateMap map : maps) {
cursor.addInput(map.cursor(null, false));
}
cursor.start();
LazyRecord record;
int expected = -1;
for (int i = 0; i < nRecords; i++) {
// debug("trial: %s, nRecords: %s, i: %s", t, nRecords, i);
// next
record = cursor.next();
// debug(" next: %s", key(record));
assertEquals(++expected, key(record));
if (expected < nRecords - 1) {
// next
record = cursor.next();
// debug(" next: %s", key(record));
assertEquals(++expected, key(record));
// previous
record = cursor.previous();
// debug(" previous: %s", key(record));
assertEquals(--expected, key(record));
} else {
assertNull(cursor.next());
}
}
}
// changing direction the other way (two steps backward, one step forward)
{
MergeCursor cursor = new MergeCursor(null, false);
for (PrivateMap map : maps) {
cursor.addInput(map.cursor(null, false));
}
cursor.start();
LazyRecord record;
int expected = nRecords;
for (int i = 0; i < nRecords; i++) {
// next
record = cursor.previous();
assertEquals(--expected, key(record));
if (expected > 0) {
// next
record = cursor.previous();
assertEquals(--expected, key(record));
// previous
record = cursor.next();
assertEquals(++expected, key(record));
} else {
assertNull(cursor.previous());
}
}
}
}
}
private MergeCursor forwardMergeCursor(SealedMap... inputs) throws IOException, InterruptedException
{
MergeCursor mergeScan = new MergeCursor(null, true);
for (SealedMap input : inputs) {
mergeScan.addInput(input.cursor(null, false));
}
mergeScan.start();
return mergeScan;
}
private MergeCursor backwardMergeCursor(SealedMap... inputs) throws IOException, InterruptedException
{
MergeCursor mergeCursor = new MergeCursor(null, false);
for (SealedMap input : inputs) {
mergeCursor.addInput(input.cursor(null, false));
}
mergeCursor.start();
return mergeCursor;
}
private AbstractKey keyOrNull(LazyRecord record) throws IOException, InterruptedException
{
return record == null ? null : record.materializeRecord().key();
}
private void debug(String template, Object ... args)
{
System.out.println(String.format(template, args));
}
private static int MAX_N = 10;
}