/*
* 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;
import com.github.geophile.erdo.apiimpl.DisklessTestDatabase;
import com.github.geophile.erdo.util.FileUtil;
import junit.framework.Assert;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.*;
public class CursorTest
{
@BeforeClass
public static void beforeClass()
{
FACTORY = new TestFactory();
}
@After
public void after()
{
FACTORY.reset();
}
@Test
public void testFindAndNext()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
for (int n = 0; n < 100; n++) {
loadDisklessDatabase(n);
Cursor cursor;
TestRecord record;
TestKey key;
int expected;
// Complete cursor
{
expected = 0;
cursor = map.first();
while ((record = (TestRecord) cursor.next()) != null) {
checkRecord(expected++, record);
}
assertEquals(n, expected);
}
// Random access
{
// Test:
// - k * GAP - GAP/2 (missing)
// - k * GAP (present if k < n, missing if k = n)
for (int k = 0; k <= n; k++) {
// Test missing
{
int missingKey = k * GAP - GAP / 2;
key = new TestKey(missingKey);
// test find -> record
record = (TestRecord) map.find(key);
assertNull(record);
// test find -> cursor
cursor = map.cursor(key);
expected = k;
while ((record = (TestRecord) cursor.next()) != null) {
checkRecord(expected++, record);
}
assertEquals(n, expected);
}
// Test present
int presentKey = k * GAP;
expected = k;
key = new TestKey(presentKey);
// test find -> record
record = (TestRecord) map.find(key);
if (k < n) {
checkRecord(expected, record);
} else {
assertEquals(n, k);
assertNull(record);
}
// test find -> cursor
cursor = map.cursor(key);
while ((record = (TestRecord) cursor.next()) != null) {
checkRecord(expected++, record);
}
assertEquals(n, expected);
}
}
}
}
@Test
public void testFindAndPrevious()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
for (int n = 0; n < 100; n++) {
// debug("n: %s", n);
loadDisklessDatabase(n);
Cursor cursor;
TestRecord record;
TestKey key;
int expected;
// Complete cursor
{
expected = n;
cursor = map.last();
while ((record = (TestRecord) cursor.previous()) != null) {
// debug("expected: %s, actual: %s", (expected - 1) * GAP, record.key());
checkRecord(--expected, record);
}
assertEquals(0, expected);
}
// Random access
{
// Test:
// - k * GAP - GAP/2 (missing)
// - k * GAP (present if k < n, missing if k = n)
for (int k = 0; k <= n; k++) {
// Test missing
{
int missingKey = k * GAP - GAP / 2;
key = new TestKey(missingKey);
// test find -> record
record = (TestRecord) map.find(key);
assertNull(record);
// test find -> cursor
cursor = map.cursor(key);
expected = k;
while ((record = (TestRecord) cursor.previous()) != null) {
checkRecord(--expected, record);
}
assertEquals(0, expected);
}
// Test present
int presentKey = k * GAP;
expected = k;
key = new TestKey(presentKey);
// test find -> record
record = (TestRecord) map.find(key);
if (k < n) {
checkRecord(expected, record);
// test find -> cursor
cursor = map.cursor(key);
while ((record = (TestRecord) cursor.previous()) != null) {
checkRecord(expected--, record);
}
assertEquals(n == 0 ? 0 : -1, expected);
} else {
assertEquals(n, k);
assertNull(record);
}
}
}
}
}
@Test
public void testClose() throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
// Test close of cursor over empty (which starts out closed)
{
loadDisklessDatabase(0);
Cursor cursor = map.first();
assertNull(cursor.next());
cursor.close();
assertNull(cursor.next());
cursor.close();
assertNull(cursor.next());
}
// Test repeated close of cursor that wasn't closed to start
{
loadDisklessDatabase(10);
Cursor cursor = map.first();
assertNotNull(cursor.next());
assertNotNull(cursor.next());
cursor.close();
assertNull(cursor.next());
cursor.close();
assertNull(cursor.next());
}
}
@Test
public void testNextAlternatingWithPrevious()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
final int N = 10;
loadDisklessDatabase(N);
Cursor cursor = map.first();
TestRecord record;
int expected = -1;
for (int i = 0; i < N; i++) {
record = (TestRecord) cursor.next();
checkRecord(++expected, record);
if (i < N - 1) {
record = (TestRecord) cursor.next();
checkRecord(++expected, record);
record = (TestRecord) cursor.previous();
checkRecord(--expected, record);
} else {
assertNull(cursor.next());
}
}
assertEquals(N - 1, expected);
}
@Test
public void testPreviousAlternatingWithNext()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
final int N = 10;
loadDisklessDatabase(N);
Cursor cursor = map.last();
TestRecord record;
int expected = N;
for (int i = 0; i < N; i++) {
record = (TestRecord) cursor.previous();
checkRecord(--expected, record);
if (i < N - 1) {
record = (TestRecord) cursor.previous();
checkRecord(--expected, record);
record = (TestRecord) cursor.next();
checkRecord(++expected, record);
} else {
assertNull(cursor.previous());
}
}
assertEquals(0, expected);
}
// Inspired by bug #5
@Test
public void multipleCursors()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
final int N = 100;
loadDatabase(N);
Cursor cursor1 = map.first();
Cursor cursor2 = map.first();
TestRecord record1;
TestRecord record2;
int expected = 0;
while (expected < N/2) {
record1 = (TestRecord) cursor1.next();
record2 = (TestRecord) cursor2.next();
checkRecord(expected, record1);
checkRecord(expected, record2);
expected++;
}
cursor1.close();
while (expected < N) {
record2 = (TestRecord) cursor2.next();
checkRecord(expected, record2);
expected++;
}
}
/*
@Test
public void closeImmediatelyAfterOpen()
throws InterruptedException, DeadlockException, TransactionRolledBackException, IOException
{
final int N = 100;
loadDatabase(N);
Cursor cursor = map.cursor(new TestKey(testKey(-1)));
map.delete(new TestKey(testKey(0)));
cursor.close();
}
*/
private void loadDisklessDatabase(int n)
throws IOException, InterruptedException, DeadlockException, TransactionRolledBackException
{
// map is loaded with (x * GAP, "r.x"), 0 <= x < n
Database db = new DisklessTestDatabase(FACTORY);
map = db.createMap(MAP_NAME, RecordFactory.simpleRecordFactory(TestKey.class, TestRecord.class));
for (int key = 0; key < n; key++) {
AbstractRecord replaced = map.put(TestRecord.createRecord(testKey(key), testValue(key)));
Assert.assertNull(replaced);
}
db.commitTransaction();
}
private void loadDatabase(int n)
throws IOException, InterruptedException, DeadlockException, TransactionRolledBackException
{
// map is loaded with (x * GAP, "r.x"), 0 <= x < n
final File DB_DIRECTORY = new File(FileUtil.tempDirectory(), DB_NAME);
FileUtil.deleteDirectory(DB_DIRECTORY);
Database db = Database.createDatabase(DB_DIRECTORY);
map = db.createMap(MAP_NAME, RecordFactory.simpleRecordFactory(TestKey.class, TestRecord.class));
for (int key = 0; key < n; key++) {
AbstractRecord replaced = map.put(TestRecord.createRecord(testKey(key), testValue(key)));
Assert.assertNull(replaced);
}
db.commitTransaction();
}
private void checkRecord(int expected, TestRecord record)
{
assertNotNull(record);
assertEquals(testKey(expected), record.key().key());
assertEquals(testValue(expected), record.stringValue());
}
private int testKey(int x)
{
return x * GAP;
}
private String testValue(int x)
{
return String.format("r.%s", x);
}
private void debug(String template, Object ... args)
{
System.out.println(String.format(template, args));
}
private static TestFactory FACTORY;
private static final String DB_NAME = "erdo";
private static final String MAP_NAME = "map";
private static final int GAP = 10;
private OrderedMap map;
}