/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004-2005
* Sleepycat Software. All rights reserved.
*
* $Id: CheckBase.java,v 1.6 2005/09/21 18:48:26 linda Exp $
*/
package com.sleepycat.je.recovery;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import junit.framework.TestCase;
import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.VerifyConfig;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.util.TestUtils;
public class CheckBase extends TestCase {
private static final boolean DEBUG = false;
private HashSet expected;
private Set found;
protected File envHome;
Environment env;
public CheckBase() {
envHome = new File(System.getProperty(TestUtils.DEST_DIR));
}
public void setUp()
throws IOException {
TestUtils.removeLogFiles("Setup", envHome, false);
}
public void tearDown()
throws Exception {
TestUtils.removeLogFiles("TearDown", envHome, false);
}
/**
* Create an environment, generate data, record the expected values.
* Then close the environment and recover, and check that the expected
* values are there.
*/
protected void testOneCase(String dbName,
EnvironmentConfig startEnvConfig,
DatabaseConfig startDbConfig,
TestGenerator testGen,
EnvironmentConfig validateEnvConfig,
DatabaseConfig validateDbConfig)
throws Throwable {
try {
/* Create an environment. */
env = new Environment(envHome, startEnvConfig);
Database db = env.openDatabase(null, dbName, startDbConfig);
/* Generate test data. */
testGen.generateData(db);
/* Scan the database to save what values we should have. */
loadExpectedData(db);
/* Close w/out checkpointing. */
db.close();
DbInternal.envGetEnvironmentImpl(env).close(false);
env = null;
/* Recover and load what's in the database post-recovery. */
recoverAndLoadData(validateEnvConfig,
validateDbConfig,
dbName);
/* Check the pre and post recovery data. */
validate();
/* Repeat the recovery and validation. */
recoverAndLoadData(validateEnvConfig,
validateDbConfig,
dbName);
/* Check the pre and post recovery data. */
validate();
} catch (Throwable t) {
/* Dump stack trace before trying to tear down. */
t.printStackTrace();
throw t;
}
}
protected void turnOffEnvDaemons(EnvironmentConfig envConfig) {
envConfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER.getName(),
"false");
envConfig.setConfigParam(EnvironmentParams.
ENV_RUN_CHECKPOINTER.getName(),
"false");
envConfig.setConfigParam(EnvironmentParams.ENV_RUN_EVICTOR.getName(),
"false");
envConfig.setConfigParam(EnvironmentParams.
ENV_RUN_INCOMPRESSOR.getName(),
"false");
}
/**
* Re-open the environment and load all data present, to compare to the
* data set of expected values.
*/
protected void recoverAndLoadData(EnvironmentConfig envConfig,
DatabaseConfig dbConfig,
String dbName)
throws DatabaseException {
env = new Environment(envHome, envConfig);
Database db = env.openDatabase(null, dbName, dbConfig);
found = new HashSet();
Cursor cursor = db.openCursor(null, null);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
int i = 0;
try {
while (cursor.getNext(key, data, null) ==
OperationStatus.SUCCESS) {
TestData t = new TestData(key, data);
if (DEBUG) {
System.out.println("found k=" +
IntegerBinding.entryToInt(key) +
" d=" +
IntegerBinding.entryToInt(data));
}
found.add(t);
}
}
finally {
cursor.close();
}
db.close();
assertTrue(env.verify(new VerifyConfig(), System.out));
env.close();
}
/*
* The found and expected data sets need to match exactly after recovery.
*/
protected void validate()
throws DatabaseException {
assertEquals("expected and found set sizes don't match" ,
expected.size(), found.size());
/* Preserve expected so we can re-use it for other validations. */
Set useExpected = (Set) expected.clone();
Iterator foundIter = found.iterator();
while (foundIter.hasNext()) {
TestData t = (TestData) foundIter.next();
assertTrue("Missing " + t + "from the expected set",
useExpected.remove(t));
}
assertEquals("Expected has " + useExpected.size() + " items remaining",
0, useExpected.size());
}
protected void putTestData(Database db,
DatabaseEntry key,
DatabaseEntry data)
throws DatabaseException {
assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
}
private void loadExpectedData(Database db)
throws DatabaseException {
expected = new HashSet();
Cursor cursor = db.openCursor(null, null);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
try {
while (cursor.getNext(key, data, null) ==
OperationStatus.SUCCESS) {
if (DEBUG) {
System.out.println("expect k=" +
IntegerBinding.entryToInt(key) +
" d=" +
IntegerBinding.entryToInt(data));
}
TestData t = new TestData(key, data);
expected.add(t);
}
}
finally {
cursor.close();
}
}
private void dumpData(Database db)
throws DatabaseException {
Cursor cursor = db.openCursor(null, null);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
int i = 0;
try {
while (cursor.getNext(key, data, null) ==
OperationStatus.SUCCESS) {
TestData t = new TestData(key, data);
System.out.println(t);
i++;
}
}
finally {
cursor.close();
}
System.out.println("scanned=" + i);
}
private void dumpExpected() {
Iterator iter = expected.iterator();
System.out.println("expected size=" + expected.size());
while (iter.hasNext()) {
System.out.println((TestData)iter.next());
}
}
protected class TestData {
private DatabaseEntry key;
private DatabaseEntry data;
TestData(DatabaseEntry key, DatabaseEntry data) {
this.key = new DatabaseEntry(key.getData());
this.data = new DatabaseEntry(data.getData());
}
public boolean equals(Object o ) {
if (this == o)
return true;
if (!(o instanceof TestData))
return false;
TestData other = (TestData) o;
if (Arrays.equals(key.getData(), other.key.getData()) &&
Arrays.equals(data.getData(), other.data.getData())) {
return true;
} else
return false;
}
public String toString() {
return " k=" + IntegerBinding.entryToInt(key) +
" d=" + IntegerBinding.entryToInt(data);
}
public int hashCode() {
return toString().hashCode();
}
}
/*
* Each unit test overrides the generateData method. Don't make this
* abstract, because we may want different unit tests to call different
* flavors of generateData(), and we want a default implementation for each
* flavor.
*/
protected class TestGenerator {
/*
* Some generators run off a list of test operations which specify
* what actions to use when generating data.
*/
public List operationList;
public TestGenerator() {
}
public TestGenerator(List operationList) {
this.operationList = operationList;
}
void generateData(Database db)
throws Exception {
}
}
}