/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009, 2013 Oracle and/or its affiliates.  All rights reserved.
 *
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using NUnit.Framework;
using BerkeleyDB;

namespace CsharpAPITest
{
	[TestFixture]
	public class BTreeDatabaseTest : DatabaseTest
	{

		[TestFixtureSetUp]
		public void SetUpTestFixture()
		{
			testFixtureName = "BTreeDatabaseTest";
			base.SetUpTestfixture();
		}

		[Test]
		public void TestCompactWithoutTxn()
		{
			int i, nRecs;
			nRecs = 1000;
			testName = "TestCompactWithoutTxn";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			// The minimum page size
			btreeDBConfig.PageSize = 512;
			btreeDBConfig.BTreeCompare =
			    new EntryComparisonDelegate(dbIntCompare);
			using (BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig))
			{
				DatabaseEntry key;
				DatabaseEntry data;

				// Fill the database with entries from 0 to 999
				for (i = 0; i < nRecs; i++)
				{
					key = new DatabaseEntry(
					    BitConverter.GetBytes(i));
					data = new DatabaseEntry(
ASCIIEncoding.ASCII.GetBytes(Configuration.RandomString(100)));
					btreeDB.Put(key, data);
				}

				/*
				 * Delete entries below 50, between 300 and
				 * 500 and above 700
				 */
				for (i = 0; i < nRecs; i++)
					if (i < (int)(nRecs * 0.05) ||
					    i > (int)(nRecs * 0.7) ||
					    (i < (int)(nRecs * 0.5) &&
					    i > (int)(nRecs * 0.3)))
					{
						key = new DatabaseEntry(
						    BitConverter.GetBytes(i));
						btreeDB.Delete(key);
					}

				btreeDB.Sync();
				long fileSize = new FileInfo(
				   btreeDBFileName).Length;

				// Compact database
				CompactConfig cCfg = new CompactConfig();
				cCfg.FillPercentage = 80;
				cCfg.Pages = 4;
				cCfg.Timeout = 1000;
				cCfg.TruncatePages = true;
				cCfg.start = new DatabaseEntry(
				    BitConverter.GetBytes(1));
				cCfg.stop = new DatabaseEntry(
				    BitConverter.GetBytes(7000));
				CompactData compactData = btreeDB.Compact(cCfg);
				// Verify output statistics fields.
				Assert.AreEqual(0, compactData.Deadlocks);
				Assert.LessOrEqual(0, compactData.EmptyBuckets);
				Assert.LessOrEqual(0, compactData.Levels);
				Assert.Less(0, compactData.PagesExamined);
				Assert.Less(0, compactData.PagesFreed);
				Assert.Less(compactData.PagesFreed,
				    compactData.PagesTruncated);

				btreeDB.Sync();
				long compactedFileSize =
				    new FileInfo(btreeDBFileName).Length;
				Assert.Less(compactedFileSize, fileSize);
			}
		}
		
                [Test]
                public void TestCompression() {
                        testName = "TestCompression";
                        SetUpTest(true);
                        string btreeDBName = testHome + "/" + testName + ".db";

                        BTreeDatabaseConfig cfg = new BTreeDatabaseConfig();
                        cfg.Creation = CreatePolicy.ALWAYS;
                        cfg.SetCompression(compress, decompress);
                        BTreeDatabase db = BTreeDatabase.Open(btreeDBName, cfg);
                        DatabaseEntry key, data;
                        char[] keyData = { 'A', 'A', 'A', 'A' };
                        data = new DatabaseEntry(
                                ASCIIEncoding.ASCII.GetBytes("abcdefghij"));
                        int i;
                        for (i = 0; i < 2000; i++) {
                                // Write random data
                                key = new DatabaseEntry(
                                        ASCIIEncoding.ASCII.GetBytes(keyData));
                                db.Put(key, data);

                                // Bump the key. Rollover from Z to A if necessary
                                int j = keyData.Length;
                                do {
                                        j--;
                                        if (keyData[j]++ == 'Z')
                                                keyData[j] = 'A';
                                } while (keyData[j] == 'A');
                       }
                        db.Close();
                }

                bool compress(DatabaseEntry prevKey, DatabaseEntry prevData, 
                    DatabaseEntry key, DatabaseEntry data, ref byte[] dest, out int size) {
                        /* 
                         * Just a dummy function that doesn't do any compression.  It just
                         * writes the 5 byte key and 10 byte data to the buffer.
                         */
                        size = key.Data.Length + data.Data.Length;
                        if (size > dest.Length)
                                return false;
                        key.Data.CopyTo(dest, 0);
                        data.Data.CopyTo(dest, key.Data.Length);
                        return true;
                }

                KeyValuePair<DatabaseEntry, DatabaseEntry> decompress(
                    DatabaseEntry prevKey, DatabaseEntry prevData, byte[] compressed, out uint bytesRead) {
                        byte[] keyData = new byte[4];
                        byte[] dataData = new byte[10];
                        Array.ConstrainedCopy(compressed, 0, keyData, 0, 4);
                        Array.ConstrainedCopy(compressed, 4, dataData, 0, 10);
                        DatabaseEntry key = new DatabaseEntry(keyData);
                        DatabaseEntry data = new DatabaseEntry(dataData);
                        bytesRead = (uint)(key.Data.Length + data.Data.Length);
                        return new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
                }

                /*
                 * Test the default compression - which is prefix compression
                 * of keys. This should work well with ordered keys and short
                 * data items.
                 */
                [Test]
                public void TestCompressionDefault() {
                        testName = "TestCompressionDefault";
                        SetUpTest(true);
                        string btreeDBName = testHome + "/" + testName + ".db";

                        BTreeDatabaseConfig cfg = new BTreeDatabaseConfig();
                        cfg.Creation = CreatePolicy.ALWAYS;
                        BTreeDatabase db = BTreeDatabase.Open(btreeDBName, cfg);
                        DatabaseEntry key, data;
                        char[] keyData = { 'A', 'A', 'A', 'A' };
                        data = new DatabaseEntry(
                                ASCIIEncoding.ASCII.GetBytes("A"));
                        int i;
                        for (i = 0; i < 5000; i++) {
                                // Write random data
                                key = new DatabaseEntry(
                                        ASCIIEncoding.ASCII.GetBytes(keyData));
                                db.Put(key, data);

                                // Bump the key.
                                int j = keyData.Length;
                                do {
                                        j--;
                                        if (keyData[j]++ == 'Z')
                                                keyData[j] = 'A';
                                } while (keyData[j] == 'A');
                        }
                        db.Close();

                        FileInfo dbInfo = new FileInfo(btreeDBName);
                        long uncompressedSize = dbInfo.Length;
                        Configuration.ClearDir(testHome);

                        cfg = new BTreeDatabaseConfig();
                        cfg.Creation = CreatePolicy.ALWAYS;
                        cfg.SetCompression();
                        db = BTreeDatabase.Open(btreeDBName, cfg);
                        keyData = new char[]{ 'A', 'A', 'A', 'A' };
                        data = new DatabaseEntry(
                                ASCIIEncoding.ASCII.GetBytes("A"));
                        for (i = 0; i < 5000; i++) {
                                // Write random data
                                key = new DatabaseEntry(
                                        ASCIIEncoding.ASCII.GetBytes(keyData));
                                db.Put(key, data);

                                // Bump the key.
                                int j = keyData.Length;
                                do {
                                        j--;
                                        if (keyData[j]++ == 'Z')
                                                keyData[j] = 'A';
                                } while (keyData[j] == 'A');
                        }
                        Cursor dbc = db.Cursor();
                        foreach (KeyValuePair<DatabaseEntry, DatabaseEntry> kvp
                                in dbc) 
                                i--;
                        dbc.Close();
                        Assert.AreEqual(i, 0);
                        db.Close();

                        dbInfo = new FileInfo(btreeDBName);
                        Assert.Less(dbInfo.Length, uncompressedSize);
                        Console.WriteLine(
                                "Uncompressed: {0}", uncompressedSize);
                        Console.WriteLine("Compressed: {0}", dbInfo.Length);

                        Configuration.ClearDir(testHome);

                        cfg = new BTreeDatabaseConfig();
                        cfg.Creation = CreatePolicy.ALWAYS;
                        cfg.SetCompression();
                        db = BTreeDatabase.Open(btreeDBName, cfg);
                        for (i = 1023; i < 1124; i++){
                                key = new DatabaseEntry(BitConverter.GetBytes(i));
                                data = new DatabaseEntry(BitConverter.GetBytes(i + 3));
                                db.Put(key, data);
                        }
                        dbc = db.Cursor();
                        foreach (KeyValuePair<DatabaseEntry, DatabaseEntry> kvp in dbc){ 
                                int keyInt = BitConverter.ToInt32(kvp.Key.Data, 0);
                                int dataInt = BitConverter.ToInt32(kvp.Value.Data, 0);
                                Assert.AreEqual(3, dataInt - keyInt);
                        }
                        dbc.Close();

                        db.Close();    
                }

		[Test, ExpectedException(typeof(AccessViolationException))]
		public void TestClose()
		{
			testName = "TestClose";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			btreeDB.Close();
			DatabaseEntry key = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("hi"));
			DatabaseEntry data = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("hi"));
			btreeDB.Put(key, data);
		}

		[Test]
		public void TestCloseWithoutSync()
		{
			testName = "TestCloseWithoutSync";
			SetUpTest(true);
			string btreeDBName = testName + ".db";

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.ForceFlush = true;
			envConfig.UseTxns = true;
			envConfig.UseMPool = true;
			envConfig.UseLogging = true;
			envConfig.LogSystemCfg = new LogConfig();
			envConfig.LogSystemCfg.ForceSync = false;
			envConfig.LogSystemCfg.AutoRemove = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			TransactionConfig txnConfig = new TransactionConfig();
			txnConfig.SyncAction =
			    TransactionConfig.LogFlush.WRITE_NOSYNC;
			Transaction txn = env.BeginTransaction(txnConfig);

			BTreeDatabaseConfig btreeConfig =
			    new BTreeDatabaseConfig();
			btreeConfig.Creation = CreatePolicy.ALWAYS;
			btreeConfig.Env = env;

			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBName, btreeConfig, txn);

			DatabaseEntry key = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key"));
			DatabaseEntry data = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("data"));
			Assert.IsFalse(btreeDB.Exists(key, txn));
			btreeDB.Put(key, data, txn);
			btreeDB.Close(false);
			txn.Commit();
			env.Close();

			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.NEVER;
			using (BTreeDatabase db = BTreeDatabase.Open(
			    testHome + "/" + btreeDBName, dbConfig))
			{
				Assert.IsFalse(db.Exists(key));
			}
		}

		[Test]
		public void TestCursorWithoutEnv()
		{
			BTreeCursor cursor;
			BTreeDatabase db;
			string dbFileName;

			testName = "TestCursorWithoutEnv";
			SetUpTest(true);
			dbFileName = testHome + "/" + testName + ".db";

			// Open btree database.
			OpenBtreeDB(null, null, dbFileName, out db);

			// Get a cursor.
			cursor = db.Cursor();

			/*
			 * Add a record to the database with cursor and 
			 * confirm that the record exists in the database.
			 */
			CursorTest.AddOneByCursor(db, cursor);

			// Close cursor and database.
			cursor.Close();
			db.Close();
		}

		[Test]
		public void TestCursorWithConfigInTxn()
		{
			BTreeCursor cursor;
			BTreeDatabase db;
			DatabaseEnvironment env;
			Transaction txn;
			string dbFileName;

			testName = "TestCursorWithConfigInTxn";
			SetUpTest(true);
			dbFileName = testName + ".db";

			// Open environment and begin a transaction.

			SetUpEnvAndTxn(testHome, out env, out txn);
			OpenBtreeDB(env, txn, dbFileName, out db);

			// Config and get a cursor.
			cursor = db.Cursor(new CursorConfig(), txn);

			/*
			 * Add a record to the database with cursor and 
			 * confirm that the record exists in the database.
			 */
			CursorTest.AddOneByCursor(db, cursor);

			/*
			 * Close cursor, database, commit the transaction 
			 * and close the environment.
			 */
			cursor.Close();
			db.Close();
			txn.Commit();
			env.Close();
		}

		[Test]
		public void TestCursorWithoutConfigInTxn()
		{
			BTreeCursor cursor;
			BTreeDatabase db;
			DatabaseEnvironment env;
			Transaction txn;
			string dbFileName;

			testName = "TestCursorWithoutConfigInTxn";
			SetUpTest(true);
			dbFileName = testName + ".db";

			// Open environment and begin a transaction.
			SetUpEnvAndTxn(testHome, out env, out txn);
			OpenBtreeDB(env, txn, dbFileName, out db);

			// Get a cursor in the transaction.
			cursor = db.Cursor(txn);

			/*
			 * Add a record to the database with cursor and 
			 * confirm that the record exists in the database.
			 */
			CursorTest.AddOneByCursor(db, cursor);

			/*
			 * Close cursor, database, commit the transaction 
			 * and close the environment.
			 */
			cursor.Close();
			db.Close();
			txn.Commit();
			env.Close();
		}

		[Test, ExpectedException(typeof(ExpectedTestException))]
		public void TestDelete()
		{
			testName = "TestDelete";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			DatabaseEntry key = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key"));
			DatabaseEntry data = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("data"));
			btreeDB.Put(key, data);
			btreeDB.Delete(key);
			try
			{
				btreeDB.Get(key);
			}
			catch (NotFoundException)
			{
				throw new ExpectedTestException();
			}
			finally
			{
				btreeDB.Close();
			}
		}

		[Test]
		public void TestDupCompare()
		{
			testName = "TestDupCompare";
			SetUpTest(true);

			BTreeDatabaseConfig cfg = new BTreeDatabaseConfig();
			cfg.Creation = CreatePolicy.IF_NEEDED;
			cfg.Duplicates = DuplicatesPolicy.UNSORTED;

			// Do not set the duplicate comparison delegate.
			using (BTreeDatabase db = BTreeDatabase.Open(
			    testHome + "/" + testName + ".db", cfg)) {
				try {
					int ret = db.DupCompare(
					    new DatabaseEntry(
					    BitConverter.GetBytes(255)),
					    new DatabaseEntry(
					    BitConverter.GetBytes(257)));
					throw new TestException();
				} catch(NullReferenceException) {
				}
			}

			/*
			 * Set the duplicate comparison delegate, and verify
			 * the comparison delegate.
			 */ 
			cfg.DuplicateCompare =
			    new EntryComparisonDelegate(dbIntCompare);
			using (BTreeDatabase db = BTreeDatabase.Open(
			    testHome + "/" + testName + "1.db", cfg)) {
				int ret = db.DupCompare(
				    new DatabaseEntry(BitConverter.GetBytes(255)),
				    new DatabaseEntry(BitConverter.GetBytes(257)));
				Assert.Greater(0, ret);
			}
		}

		[Test]
		public void TestEncryption() {
			testName = "TestEncryption";
			SetUpTest(true);

			BTreeDatabase db1;

			BTreeDatabaseConfig dbCfg =
			    new BTreeDatabaseConfig();
			dbCfg.Creation = CreatePolicy.IF_NEEDED;

			// Open an encrypted database.
			dbCfg.SetEncryption("bdb", EncryptionAlgorithm.AES);
			using (db1 = BTreeDatabase.Open(
			    testHome + "/" + testName + ".db", dbCfg)) {
				Assert.IsTrue(db1.Encrypted);
				for (int i = 0; i < 10; i++)
					db1.Put(new DatabaseEntry
					    (BitConverter.GetBytes(i)),
					    new DatabaseEntry(
					    BitConverter.GetBytes(i)));
			}

			// Verify the database is encrypted.
			BTreeDatabaseConfig verifyDbCfg =
			    new BTreeDatabaseConfig();
			verifyDbCfg.Creation = CreatePolicy.IF_NEEDED;
			verifyDbCfg.SetEncryption(
			    dbCfg.EncryptionPassword,
			    dbCfg.EncryptAlgorithm);
			verifyDbCfg.Encrypted = true;
			using (db1 = BTreeDatabase.Open(
			    testHome + "/" + testName + ".db",
			    verifyDbCfg)) {
				for (int i = 0; i < 10; i++)
					db1.Get(new DatabaseEntry(
					    BitConverter.GetBytes(i)));
			};
		}

		[Test]
		public void TestExist()
		{
			testName = "TestExist";
			SetUpTest(true);
			string dbFileName = testHome + "/" + testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.IF_NEEDED;
			BTreeDatabase btreeDB;
			using (btreeDB = BTreeDatabase.Open(
			    dbFileName, btreeDBConfig))
			{
				DatabaseEntry key = new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("key"));
				DatabaseEntry data = new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("data"));

				btreeDB.Put(key, data);
				Assert.IsTrue(btreeDB.Exists(
				     new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("key"))));
				Assert.IsFalse(btreeDB.Exists(
				     new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("data"))));
			}
		}

		[Test]
		public void TestExistWithTxn()
		{
			BTreeDatabase btreeDB;
			Transaction txn;
			DatabaseEnvironmentConfig envConfig;
			DatabaseEnvironment env;
			DatabaseEntry key, data;

			testName = "TestExistWithTxn";
			SetUpTest(true);

			// Open an environment.
			envConfig = new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseMPool = true;
			envConfig.UseTxns = true;
			env = DatabaseEnvironment.Open(testHome, envConfig);

			// Begin a transaction.
			txn = env.BeginTransaction();

			// Open a database.
			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.IF_NEEDED;
			btreeDBConfig.Env = env;
			btreeDB = BTreeDatabase.Open(testName + ".db",
			    btreeDBConfig, txn);

			// Put key data pair into database.
			key = new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("key"));
			data = new DatabaseEntry(
			     ASCIIEncoding.ASCII.GetBytes("data"));
			btreeDB.Put(key, data, txn);

			// Confirm that the pair exists in the database.
			Assert.IsTrue(btreeDB.Exists(new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key")), txn));
			Assert.IsFalse(btreeDB.Exists(new DatabaseEntry(
			     ASCIIEncoding.ASCII.GetBytes("data")), txn));

			// Dispose all.
			btreeDB.Close();
			txn.Commit();
			env.Close();
		}

		[Test]
		public void TestExistWithLockingInfo()
		{
			BTreeDatabase btreeDB;
			DatabaseEnvironment env;
			DatabaseEntry key, data;

			testName = "TestExistWithLockingInfo";
			SetUpTest(true);

			// Open the environment.
			DatabaseEnvironmentConfig envCfg =
			    new DatabaseEnvironmentConfig();
			envCfg.Create = true;
			envCfg.FreeThreaded = true;
			envCfg.UseLocking = true;
			envCfg.UseLogging = true;
			envCfg.UseMPool = true;
			envCfg.UseTxns = true;
			env = DatabaseEnvironment.Open(
			    testHome, envCfg);

			// Open database in transaction.
			Transaction openTxn = env.BeginTransaction();
			BTreeDatabaseConfig cfg =
			    new BTreeDatabaseConfig();
			cfg.Creation = CreatePolicy.ALWAYS;
			cfg.Env = env;
			cfg.FreeThreaded = true;
			cfg.PageSize = 4096;
			cfg.Duplicates = DuplicatesPolicy.UNSORTED;
			btreeDB = BTreeDatabase.Open(testName + ".db",
			    cfg, openTxn);
			openTxn.Commit();

			// Put key data pair into database.
			Transaction txn = env.BeginTransaction();
			key = new DatabaseEntry(
				     ASCIIEncoding.ASCII.GetBytes("key"));
			data = new DatabaseEntry(
			     ASCIIEncoding.ASCII.GetBytes("data"));
			btreeDB.Put(key, data, txn);

			// Confirm that the pair exists in the database with LockingInfo.
			LockingInfo lockingInfo = new LockingInfo();
			lockingInfo.ReadModifyWrite = true;

			// Confirm that the pair exists in the database.
			Assert.IsTrue(btreeDB.Exists(new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key")), txn, lockingInfo));
			Assert.IsFalse(btreeDB.Exists(new DatabaseEntry(
			     ASCIIEncoding.ASCII.GetBytes("data")), txn, lockingInfo));
			txn.Commit();

			btreeDB.Close();
			env.Close();
		}

		[Test]
		public void TestGetByKey()
		{
			testName = "TestGetByKey";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
				     testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBName, btreeDBConfig);

			DatabaseEntry key = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key"));
			DatabaseEntry data = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("data"));
			btreeDB.Put(key, data);

			KeyValuePair<DatabaseEntry, DatabaseEntry> pair =
			    new KeyValuePair<DatabaseEntry, DatabaseEntry>();
			pair = btreeDB.Get(key);
			Assert.AreEqual(pair.Key.Data, key.Data);
			Assert.AreEqual(pair.Value.Data, data.Data);
			btreeDB.Close();
		}

		[Test]
		public void TestGetByRecno()
		{
			testName = "TestGetByRecno";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.UseRecordNumbers = true;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBName, btreeDBConfig);
			Assert.IsTrue(btreeDB.RecordNumbers);

			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry data = new DatabaseEntry();
			uint recno, count, value;
			for (recno = 1; recno <= 100; recno++)
			{
				value = 200 - recno;
				Configuration.dbtFromString(key,
				    Convert.ToString(value));
				Configuration.dbtFromString(data,
				    Convert.ToString(value));
				btreeDB.Put(key, data);
			}

			KeyValuePair<DatabaseEntry, DatabaseEntry> pair =
			    new KeyValuePair<DatabaseEntry, DatabaseEntry>();

			for (count = 1; ; count++)
			{
				try
				{
					pair = btreeDB.Get(count);
				}
				catch (NotFoundException)
				{
					Assert.AreEqual(101, count);
					break;
				}
				value = 299 - 200 + count;
				Assert.AreEqual(value.ToString(),
				    Configuration.strFromDBT(pair.Key));
			}

			btreeDB.Close();
		}

		[Test, ExpectedException(typeof(NotFoundException))]
		public void TestGetBoth()
		{
			testName = "TestGetBoth";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			using (BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBName, btreeDBConfig))
			{
				DatabaseEntry key = new DatabaseEntry();
				DatabaseEntry data = new DatabaseEntry();

				Configuration.dbtFromString(key, "key");
				Configuration.dbtFromString(data, "data");
				btreeDB.Put(key, data);
				KeyValuePair<DatabaseEntry, DatabaseEntry> pair =
				    new KeyValuePair<DatabaseEntry, DatabaseEntry>();
				pair = btreeDB.GetBoth(key, data);
				Assert.AreEqual(key.Data, pair.Key.Data);
				Assert.AreEqual(data.Data, pair.Value.Data);

				Configuration.dbtFromString(key, "key");
				Configuration.dbtFromString(data, "key");
				btreeDB.GetBoth(key, data);
			}
		}

		[Test]
		public void TestGetBothMultiple()
		{
			testName = "TestGetBothMultiple";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName = testName;
			DatabaseEntry key, data;
			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> kvp;
			int cnt;

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Duplicates = DuplicatesPolicy.UNSORTED;
			btreeDBConfig.PageSize = 1024;
			using (BTreeDatabase btreeDB = GetMultipleDB(
			    btreeDBFileName, btreeDBName, btreeDBConfig)) {
				key = new DatabaseEntry(BitConverter.GetBytes(100));
				data = new DatabaseEntry(BitConverter.GetBytes(100));

				kvp = btreeDB.GetBothMultiple(key, data);
				cnt = 0;
				foreach (DatabaseEntry dbt in kvp.Value)
					cnt++;
				Assert.AreEqual(cnt, 10);

				kvp = btreeDB.GetBothMultiple(key, data, 1024);
				cnt = 0;
				foreach (DatabaseEntry dbt in kvp.Value)
					cnt++;
				Assert.AreEqual(cnt, 10);
			}
		}
		
		[Test]
		public void TestGetMultiple() 
		{
			testName = "TestGetMultiple";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName = testName;

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Duplicates = DuplicatesPolicy.UNSORTED;
			btreeDBConfig.PageSize = 512;
			
			using (BTreeDatabase btreeDB = GetMultipleDB(
			    btreeDBFileName, btreeDBName, btreeDBConfig)) {
				DatabaseEntry key = new DatabaseEntry(
                                    BitConverter.GetBytes(10));
				KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> kvp = 
                                    btreeDB.GetMultiple(key, 1024);
				int cnt = 0;
				foreach (DatabaseEntry dbt in kvp.Value)
					cnt++;
				Assert.AreEqual(cnt, 10);

				key = new DatabaseEntry(
                                    BitConverter.GetBytes(102));
				kvp = btreeDB.GetMultiple(key, 1024);
				cnt = 0;
				foreach (DatabaseEntry dbt in kvp.Value)
					cnt++;
				Assert.AreEqual(cnt, 1);
			}
		}

		[Test]
		public void TestGetMultipleByRecno()
		{
			testName = "TestGetMultipleByRecno";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Duplicates = DuplicatesPolicy.NONE;
			btreeDBConfig.UseRecordNumbers = true;
			using (BTreeDatabase btreeDB = GetMultipleDB(
			    btreeDBFileName, btreeDBName, btreeDBConfig)) {
				int recno = 44;
				KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> kvp = 
                                    btreeDB.GetMultiple((uint)recno);
				int cnt = 0;
				int kdata = BitConverter.ToInt32(kvp.Key.Data, 0);
				Assert.AreEqual(kdata, recno);
				foreach (DatabaseEntry dbt in kvp.Value) {
					cnt++;
					int ddata = BitConverter.ToInt32(dbt.Data, 0);
					Assert.AreEqual(ddata, recno);
				}
				Assert.AreEqual(cnt, 1);
			}
		}

		[Test]
		public void TestGetMultipleByRecnoInSize()
		{
			testName = "TestGetMultipleByRecnoInSize";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Duplicates = DuplicatesPolicy.NONE;
			btreeDBConfig.UseRecordNumbers = true;
			btreeDBConfig.PageSize = 512;
			using (BTreeDatabase btreeDB = GetMultipleDB(
			    btreeDBFileName, btreeDBName, btreeDBConfig)) {
				int recno = 100;
				int bufferSize = 1024;
				KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> kvp = 
                                    btreeDB.GetMultiple((uint)recno, bufferSize);
				int cnt = 0;
				int kdata = BitConverter.ToInt32(kvp.Key.Data, 0);
				Assert.AreEqual(kdata, recno);
				foreach (DatabaseEntry dbt in kvp.Value) {
					cnt++;
					Assert.AreEqual(dbt.Data.Length, 111);
				}
				Assert.AreEqual(1, cnt);
			}
		}

		[Test]
		public void TestGetMultipleInSize()
		{
			testName = "TestGetMultipleInSize";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Duplicates = DuplicatesPolicy.UNSORTED;
			btreeDBConfig.PageSize = 1024;
			using (BTreeDatabase btreeDB = GetMultipleDB(
			    btreeDBFileName, btreeDBName, btreeDBConfig)) {

				int num = 101;
				DatabaseEntry key = new DatabaseEntry(
                                    BitConverter.GetBytes(num));
				int bufferSize = 10240;
				KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> kvp = 
                                    btreeDB.GetMultiple(key, bufferSize);
				int cnt = 0;
				foreach (DatabaseEntry dbt in kvp.Value) {
					cnt++;
					Assert.AreEqual(BitConverter.ToInt32(
                                            dbt.Data, 0), num);
					num++;
				}
				Assert.AreEqual(cnt, 923);
			}
		}
        
		[Test]
		public void TestGetWithTxn()
		{
			testName = "TestGetWithTxn";
			SetUpTest(true);

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseLogging = true;
			envConfig.UseMPool = true;
			envConfig.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			try
			{
				Transaction openTxn = env.BeginTransaction();
				BTreeDatabaseConfig dbConfig =
				    new BTreeDatabaseConfig();
				dbConfig.Env = env;
				dbConfig.Creation = CreatePolicy.IF_NEEDED;
				BTreeDatabase db = BTreeDatabase.Open(
				    testName + ".db", dbConfig, openTxn);
				openTxn.Commit();

				Transaction putTxn = env.BeginTransaction();
				try
				{
					for (int i = 0; i < 20; i++)
						db.Put(new DatabaseEntry(
						    BitConverter.GetBytes(i)),
						    new DatabaseEntry(
						    BitConverter.GetBytes(i)), putTxn);
					putTxn.Commit();
				}
				catch (DatabaseException e)
				{
					putTxn.Abort();
					db.Close();
					throw e;
				}

				Transaction getTxn = env.BeginTransaction();
				KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
				try
				{
					for (int i = 0; i < 20; i++)
					{
						pair = db.Get(new DatabaseEntry(
						    BitConverter.GetBytes(i)), getTxn);
						Assert.AreEqual(BitConverter.GetBytes(i),
						    pair.Key.Data);
					}

					getTxn.Commit();
					db.Close();
				}
				catch (DatabaseException)
				{
					getTxn.Abort();
					db.Close();
					throw new TestException();
				}
			}
			catch (DatabaseException)
			{
			}
			finally
			{
				env.Close();
			}
		}

		[Test]
		public void TestKeyRange()
		{
			testName = "TestKeyRange";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName = Path.GetFileNameWithoutExtension(
			    btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBName, btreeDBConfig);

			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry data = new DatabaseEntry();
			uint recno;
			for (recno = 1; recno <= 10; recno++)
			{
				Configuration.dbtFromString(key,
				    Convert.ToString(recno));
				Configuration.dbtFromString(data,
				    Convert.ToString(recno));
				btreeDB.Put(key, data);
			}

			Configuration.dbtFromString(key, Convert.ToString(5));
			KeyRange keyRange = btreeDB.KeyRange(key);
			Assert.AreEqual(0.5, keyRange.Less);
			Assert.AreEqual(0.1, keyRange.Equal);
			Assert.AreEqual(0.4, keyRange.Greater);

			btreeDB.Close();
		}

		[Test]
		public void TestNoWaitDbExclusiveLock()
		{
			testName = "TestNoWaitDbExclusiveLock";
			SetUpTest(true);

			// Open an environment.
			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.AutoCommit = true;
			envConfig.Create = true;
			envConfig.UseMPool = true;
			envConfig.UseLocking = true;
			envConfig.UseLogging = true;
			envConfig.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			// Open a database.
			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Env = env;
 			string btreeDBFileName = testName + ".db";
			BTreeDatabase btreeDB;

 			btreeDBConfig.Creation = CreatePolicy.IF_NEEDED;
			btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			Assert.IsNull(btreeDB.NoWaitDbExclusiveLock);
			btreeDB.Close();

			btreeDBConfig.NoWaitDbExclusiveLock = false;
			btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			Assert.AreEqual(btreeDB.NoWaitDbExclusiveLock, false);
			btreeDB.Close();

			btreeDBConfig.NoWaitDbExclusiveLock = true;
			btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			Assert.AreEqual(btreeDB.NoWaitDbExclusiveLock, true);
			btreeDB.Close();
			env.Close();
		}

		[Test]
		public void TestOpenExistingBtreeDB()
		{
			testName = "TestOpenExistingBtreeDB";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			     testName + ".db";

			BTreeDatabaseConfig btreeConfig =
			    new BTreeDatabaseConfig();
			btreeConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeConfig);
			btreeDB.Close();

			DatabaseConfig dbConfig = new DatabaseConfig();
			Database db = Database.Open(btreeDBFileName, 
			    dbConfig);
			Assert.AreEqual(db.Type, DatabaseType.BTREE);
			Assert.AreEqual(db.Creation, CreatePolicy.NEVER);
			db.Close();
		}

		[Test]
		public void TestOpenNewBtreeDB()
		{
			testName = "TestOpenNewBtreeDB";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			XmlElement xmlElem = Configuration.TestSetUp(
			    testFixtureName, testName);
			BTreeDatabaseConfig btreeConfig =
			    new BTreeDatabaseConfig();
			BTreeDatabaseConfigTest.Config(xmlElem,
			    ref btreeConfig, true);
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeConfig);
			Confirm(xmlElem, btreeDB, true);
			btreeDB.Close();
		}

		[Test]
		public void TestOpenMulDBInSingleFile()
		{
			testName = "TestOpenMulDBInSingleFile";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string[] btreeDBArr = new string[4];

			for (int i = 0; i < 4; i++)
				btreeDBArr[i] = Path.GetFileNameWithoutExtension(
				    btreeDBFileName) + i;

			Configuration.ClearDir(testHome);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.IF_NEEDED;

			BTreeDatabase btreeDB;
			for (int i = 0; i < 4; i++)
			{
				btreeDB = BTreeDatabase.Open(btreeDBFileName,
				    btreeDBArr[i], btreeDBConfig);
				Assert.AreEqual(CreatePolicy.IF_NEEDED, btreeDB.Creation);
				btreeDB.Close();
			}

			DatabaseConfig dbConfig = new DatabaseConfig();
			Database db;
			for (int i = 0; i < 4; i++)
			{
				using (db = Database.Open(btreeDBFileName,
				    btreeDBArr[i], dbConfig))
				{
					Assert.AreEqual(btreeDBArr[i],
					    db.DatabaseName);
					Assert.AreEqual(DatabaseType.BTREE,
					    db.Type);
				}
			}
		}

		[Test]
		public void TestOpenWithTxn()
		{
			testName = "TestOpenWithTxn";
			SetUpTest(true);
			string btreeDBName = testName + ".db";

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseTxns = true;
			envConfig.UseMPool = true;

			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);
			Transaction txn = env.BeginTransaction(
			    new TransactionConfig());

			BTreeDatabaseConfig btreeConfig =
			    new BTreeDatabaseConfig();
			btreeConfig.Creation = CreatePolicy.ALWAYS;
			btreeConfig.Env = env;

			/*
			 * If environmnet home is set, the file name in Open()
			 * is the relative path.
			 */
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBName, btreeConfig, txn);
			Assert.IsTrue(btreeDB.Transactional);
			btreeDB.Close();
			txn.Commit();
			env.Close();
		}

		[Test]
		public void TestPartialGet() 
		{
			testName = "TestPartialGet";
			SetUpTest(true);

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseTxns = true;
			envConfig.UseMPool = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			BTreeDatabase db;
			PopulateDb(env, out db);

			// Partially get the first byte and verify it is 'h'.
			DatabaseEntry key, data;
			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
			Transaction txn = env.BeginTransaction();
			for (int i = 0; i < 100; i++) {
				key = new DatabaseEntry(BitConverter.GetBytes(i));
				data = new DatabaseEntry(0, 1);
				pair = db.Get(key, data, txn);
				Assert.AreEqual(1, pair.Value.Data.Length);
				Assert.AreEqual((byte)'h', pair.Value.Data[0]);
			}
			txn.Commit();
			db.Close();
			env.Close();

			/*
			 * Open the existing database and partially get
			 * non-existing data.
			 */
			using (Database edb = Database.Open(
			    testHome + "/" + testName + ".db", 
			    new DatabaseConfig())) {
				key = new DatabaseEntry(BitConverter.GetBytes(0));
				data = new DatabaseEntry(10, 1);
				pair = edb.Get(key, data);
				Assert.AreEqual(null, pair.Value.Data);
			}
		}

		[Test]
		public void TestPartialPut() 
		{
			testName = "TestPartialPut";
			SetUpTest(true);

			BTreeDatabase db;
			PopulateDb(null, out db);

			// Partially update the records.
			DatabaseEntry key, data;
			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
			byte[] partialBytes = ASCIIEncoding.ASCII.GetBytes("aa");
			byte[] valueBytes;
			for (int i = 0; i < 100; i++) {
				key = new DatabaseEntry(BitConverter.GetBytes(i));
				if (i < 50)
					data = new DatabaseEntry(
					    partialBytes, 0, 2);
				else {
					data = new DatabaseEntry(partialBytes);
					data.Partial = true;
					data.PartialLen = 2;
					data.PartialOffset = 0;
				}
				Assert.AreEqual(0, data.PartialOffset);
				Assert.AreEqual(2, data.PartialLen);
				Assert.AreEqual(true, data.Partial);
				db.Put(key, data);
			}
			// Verify that the records have been partially changed.
			valueBytes = ASCIIEncoding.ASCII.GetBytes("hello");
			valueBytes[0] = (byte)'a';
			valueBytes[1] = (byte)'a';
			for (int i = 0; i < 100; i++) {
				key = new DatabaseEntry(BitConverter.GetBytes(i));
				pair = db.Get(key);
				Assert.AreEqual(
				    BitConverter.GetBytes(i), pair.Key.Data);
				Assert.AreEqual(valueBytes, pair.Value.Data);
			}

			// Partial put from non-existing offset.
			key = new DatabaseEntry(BitConverter.GetBytes(0));
			data = new DatabaseEntry(partialBytes, 100, 2);
			db.Put(key, data);

			// Verify that the records have been partially changed.
			pair = db.Get(key);
			Assert.AreEqual(102, pair.Value.Data.Length);
			Assert.AreEqual(valueBytes[0],
			    pair.Value.Data[100]);
			Assert.AreEqual(valueBytes[0], 
			    pair.Value.Data[101]);

			/*
			 * Reuse the DatabaseEntry for non-partial. The partial
			 * length and offset should be ignored.
			 */ 
			key = new DatabaseEntry(BitConverter.GetBytes(0));
			data.Partial = false;
			db.Put(key, data);
			pair = db.Get(key);
			Assert.AreEqual(2, pair.Value.Data.Length);

			db.Close();
		}

		private void PopulateDb(DatabaseEnvironment env,
		    out BTreeDatabase db) 
		{
			DatabaseEntry key, data;
			Transaction txn = null;
			string dbName;

			if (env != null) {
				txn = env.BeginTransaction();
				dbName = testName + ".db";
			} else
				dbName = testHome + "/" + testName + ".db";

			OpenBtreeDB(env, txn, dbName, out db);

			for (int i = 0; i < 100; i++) {
				key = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				data = new DatabaseEntry(
				    ASCIIEncoding.ASCII.GetBytes("hello"));
				db.Put(key, data, txn);
			}

			if (txn != null)
				txn.Commit();
		}

		[Test]
		public void TestPrefixCompare()
		{
			testName = "TestPrefixCompare";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			     testName + ".db";

			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.IF_NEEDED;
			dbConfig.Duplicates = DuplicatesPolicy.SORTED;
			dbConfig.BTreeCompare =
			    new EntryComparisonDelegate(dbIntCompare);
			dbConfig.BTreePrefixCompare =
			    new EntryPrefixComparisonDelegate(dbPrefixCompare);
			BTreeDatabase db = BTreeDatabase.Open(
			    btreeDBFileName, dbConfig);

			Assert.AreEqual(3, db.PrefixCompare(new DatabaseEntry(
			    BitConverter.GetBytes((Int16)255)), new DatabaseEntry(
			    BitConverter.GetBytes((Int32)65791))));

			Assert.AreEqual(1, db.PrefixCompare(new DatabaseEntry(
			BitConverter.GetBytes((Int64)255)), new DatabaseEntry(
			BitConverter.GetBytes((Int64)4294967552))));

			db.Close();
		}

		[Test]
		public void TestPutMultiple()
		{
			testName = "TestPutMultiple";
			SetUpTest(true);

			PutMultiple(null);

			Configuration.ClearDir(testHome);
			DatabaseEnvironmentConfig cfg =
			    new DatabaseEnvironmentConfig();
			cfg.Create = true;
			cfg.UseLogging = true;
			cfg.UseMPool = true;
			cfg.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, cfg);
			PutMultiple(env);
			env.Close();
		}

		private void PutMultiple(DatabaseEnvironment env)
		{
			List<DatabaseEntry> kList = new List<DatabaseEntry>();
			List<DatabaseEntry> vList = new List<DatabaseEntry>();
			BTreeDatabase db;
			DatabaseEntry key, value;
			Transaction txn;
			string dbFileName = (env == null) ? testHome + 
			    "/" + testName + ".db" : testName + ".db";
			int i;

			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.IF_NEEDED;
			if (env != null) {
				dbConfig.Env = env;
				txn = env.BeginTransaction();
				db = BTreeDatabase.Open(
				    dbFileName, dbConfig, txn);
				txn.Commit();
			} else
				db = BTreeDatabase.Open(dbFileName, dbConfig);

			for (i = 0; i < 100; i++) {
				key = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				value = new DatabaseEntry(
				    ASCIIEncoding.ASCII.GetBytes("data" + i + 
				    Configuration.RandomString(512)));
				kList.Add(key);
				vList.Add(value);
			}

			// Create bulk buffer for non-recno based keys.
			MultipleDatabaseEntry keyBuff =
			    new MultipleDatabaseEntry(kList, false);
			Assert.IsFalse(keyBuff.Recno);

			// Create bulk buffer for values.
			MultipleDatabaseEntry valBuff =
			    new MultipleDatabaseEntry(vList, false);
			i = 0;
			foreach (DatabaseEntry dbt in valBuff) {
				Assert.AreEqual(vList[i].Data, dbt.Data);
				i++;
			}
			Assert.AreEqual(100, i);

			// Create bulk buffer from another key buffer.
			MultipleDatabaseEntry keyBuff1 =
			    new MultipleDatabaseEntry(
			    keyBuff.Data, keyBuff.Recno);
			i = 0;
			foreach (DatabaseEntry dbt in keyBuff1) {
				Assert.AreEqual(kList[i].Data, dbt.Data);
				i++;
			}
			Assert.AreEqual(100, i);

			if (env != null) {
				txn = env.BeginTransaction();
				db.Put(keyBuff, valBuff, txn);
				Assert.AreEqual(100, db.Truncate(txn));
				txn.Commit();
			} else {
				/*
				 * Bulk insert to database with key and value
				 * buffers.
				 */ 				 
				db.Put(keyBuff, valBuff);

				// Verify all records exist as expected.
				Cursor cursor = db.Cursor();
				i = 99;
				Assert.IsTrue(cursor.MoveLast());
				Assert.AreEqual(kList[i].Data,
				    cursor.Current.Key.Data);
				Assert.AreEqual(vList[i].Data,
				    cursor.Current.Value.Data);
				while (cursor.MovePrev()) {
					i--;
					Assert.AreEqual(kList[i].Data,
					    cursor.Current.Key.Data);
					Assert.AreEqual(vList[i].Data,
					    cursor.Current.Value.Data);
				}
				Assert.AreEqual(0, i);
				cursor.Close();
				/*
				 * Dumped all records. The number of records
				 * should be 100.
				 */ 				 
				Assert.AreEqual(100, db.Truncate());

				/* 
				 * Bulk insert to database with a copied key
				 * buffer and a value buffer.
				 */ 
				db.Put(keyBuff1, valBuff);
				cursor = db.Cursor();
				Assert.IsTrue(cursor.MoveLast());
				i = 99;
				Assert.AreEqual(kList[i].Data,
				    cursor.Current.Key.Data);
				Assert.AreEqual(vList[i].Data,
				    cursor.Current.Value.Data);
				while (cursor.MovePrev()) {
					i--;
					Assert.AreEqual(kList[i].Data,
					    cursor.Current.Key.Data);
					Assert.AreEqual(vList[i].Data,
					    cursor.Current.Value.Data);
				}
				cursor.Close();
				Assert.AreEqual(0, i);
				/*
				 * Dumped all records. The number of records
				 * should be 100.
				 */ 				 
				Assert.AreEqual(100, db.Truncate());
			}

			db.Close();
		}

		[Test]
		public void TestPutMultipleKey()
		{
			testName = "TestPutMultipleKey";
			SetUpTest(true);

			PutMultipleKey(null);

			Configuration.ClearDir(testHome);
			DatabaseEnvironmentConfig cfg =
			    new DatabaseEnvironmentConfig();
			cfg.Create = true;
			cfg.UseLogging = true;
			cfg.UseMPool = true;
			cfg.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, cfg);
			PutMultipleKey(env);
			env.Close();
		}

		private void PutMultipleKey(DatabaseEnvironment env)
		{
			List<KeyValuePair<DatabaseEntry, DatabaseEntry>> pList =
			    new List<KeyValuePair<
			    DatabaseEntry, DatabaseEntry>>();
			BTreeDatabase db;
			DatabaseEntry key, value;
			Transaction txn;
			string dbFileName = (env == null) ? testHome +
			    "/" + testName + ".db" : testName + ".db";
			int i;

			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.IF_NEEDED;
			if (env != null) {
				dbConfig.Env = env;
				txn = env.BeginTransaction();
				db = BTreeDatabase.Open(
				    dbFileName, dbConfig, txn);
				txn.Commit();
			} else
				db = BTreeDatabase.Open(dbFileName, dbConfig);
			
			for (i = 0; i < 100; i++) {
				key = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				value = new DatabaseEntry(
				    ASCIIEncoding.ASCII.GetBytes("data" + 
				    i + Configuration.RandomString(512)));
				pList.Add(new KeyValuePair<
				    DatabaseEntry, DatabaseEntry>(key, value));
			}

			// Create btree bulk buffer for key/value pairs.
			MultipleKeyDatabaseEntry pairBuff =
			    new MultipleKeyDatabaseEntry(pList, false);
			i = 0;
			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
			    pair in pairBuff) {
				Assert.AreEqual(pList[i].Key.Data,
				    pair.Key.Data);
				Assert.AreEqual(pList[i].Value.Data,
				    pair.Value.Data);
				i++;
			}
			Assert.AreEqual(100, i);

			/*
			 * Create bulk buffer from another key/value pairs
			 * bulk buffer.
			 */ 			 
			MultipleKeyDatabaseEntry pairBuff1 =
			    new MultipleKeyDatabaseEntry(
			    pairBuff.Data, false);
			Assert.AreEqual(false, pairBuff1.Recno);
			i = 0;
			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
			    pair in pairBuff1) {
				Assert.AreEqual(pList[i].Key.Data, 
				    pair.Key.Data);
				Assert.AreEqual(pList[i].Value.Data,
				    pair.Value.Data);
				i++;
			}
			Assert.AreEqual(100, i);

			if (env == null) {
				// Bulk insert with key/value pair bulk buffer.
				db.Put(pairBuff);
				Cursor cursor = db.Cursor();
				Assert.IsTrue(cursor.MoveFirst());
				i = 0;
				Assert.AreEqual(pList[i].Key.Data,
				     cursor.Current.Key.Data);
				Assert.AreEqual(pList[i].Value.Data,
				     cursor.Current.Value.Data);
				while (cursor.MoveNext()) {
					i++;
					Assert.AreEqual(pList[i].Key.Data,
					    cursor.Current.Key.Data);
					Assert.AreEqual(pList[i].Value.Data,
					    cursor.Current.Value.Data);
				}
				Assert.AreEqual(99, i);
				cursor.Close();
				/*
				 * Dump all records from the database. The 
				 * number of records should be 100.
				 */ 				 
				Assert.AreEqual(100, db.Truncate());

				// Bulk insert with copied key/value pair buffer.
				db.Put(pairBuff1);
				cursor = db.Cursor();
				Assert.IsTrue(cursor.MoveFirst());
				i = 0;
				Assert.AreEqual(pList[i].Key.Data,
				    cursor.Current.Key.Data);
				Assert.AreEqual(pList[i].Value.Data,
				    cursor.Current.Value.Data);
				while (cursor.MoveNext()) {
					i++;
					Assert.AreEqual(pList[i].Key.Data,
					    cursor.Current.Key.Data);
					Assert.AreEqual(pList[i].Value.Data,
					    cursor.Current.Value.Data);
				}
				Assert.AreEqual(99, i);
				cursor.Close();
				Assert.AreEqual(100, db.Truncate());
			} else {
				txn = env.BeginTransaction();
				db.Put(pairBuff, txn);
				Assert.AreEqual(100, db.Truncate(txn));
				txn.Commit();
			}

			db.Close();
		}

		[Test]
		public void TestPutWithoutTxn()
		{
			testName = "TestPutWithoutTxn";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			DatabaseEntry key, data;
			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
			using (BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig))
			{
				// Put integer into database
				key = new DatabaseEntry(
				    BitConverter.GetBytes((int)0));
				data = new DatabaseEntry(
				    BitConverter.GetBytes((int)0));
				btreeDB.Put(key, data);
				pair = btreeDB.Get(key);
				Assert.AreEqual(key.Data, pair.Key.Data);
				Assert.AreEqual(data.Data, pair.Value.Data);

 				// Partial put to update the existing pair.
				data.Partial = true;
				data.PartialLen = 4;
				data.PartialOffset = 4;
				btreeDB.Put(key, data);
				pair = btreeDB.Get(key);
				Assert.AreEqual(key.Data, pair.Key.Data);
				Assert.AreEqual(8, pair.Value.Data.Length);
			}
		}

		[Test, ExpectedException(typeof(KeyExistException))]
		public void TestPutNoOverWrite()
		{
			testName = "TestPutNoOverWrite";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			DatabaseEntry key, data, newData;
			using (BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig))
			{
				key = new DatabaseEntry(
				    BitConverter.GetBytes((int)0));
				data = new DatabaseEntry(
				    BitConverter.GetBytes((int)0));
				newData = new DatabaseEntry(
				    BitConverter.GetBytes((int)1));

				btreeDB.Put(key, data);
				btreeDB.PutNoOverwrite(key, newData);
			}
		}

		[Test, ExpectedException(typeof(ExpectedTestException))]
		public void TestPutNoDuplicateWithTxn()
		{
			testName = "TestPutNoDuplicateWithTxn";
			SetUpTest(true);

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseLogging = true;
			envConfig.UseMPool = true;
			envConfig.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			Transaction txn = env.BeginTransaction();
			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.Env = env;
			btreeDBConfig.Duplicates = DuplicatesPolicy.SORTED;

			DatabaseEntry key, data;
			try
			{
				using (BTreeDatabase btreeDB =
				    BTreeDatabase.Open(
				    testName + ".db", btreeDBConfig, txn))
				{
					key = new DatabaseEntry(
					    BitConverter.GetBytes((int)0));
					data = new DatabaseEntry(
					    BitConverter.GetBytes((int)0));

					btreeDB.Put(key, data, txn);
					btreeDB.PutNoDuplicate(key, data, txn);
				}
				txn.Commit();
			}
			catch (KeyExistException)
			{
				txn.Abort();
				throw new ExpectedTestException();
			}
			finally
			{
				env.Close();
			}
		}

		[Test, ExpectedException(typeof(ExpectedTestException))]
		public void TestRemoveDBFile()
		{
			testName = "TestRemoveDBFile";
			SetUpTest(true);
			

			RemoveDBWithoutEnv(testHome, testName, false);
		}

		[Test, ExpectedException(typeof(ExpectedTestException))]
		public void TestRemoveOneDBFromDBFile()
		{
			testName = "TestRemoveOneDBFromDBFile";
			SetUpTest(true);

			RemoveDBWithoutEnv(testHome, testName, true);
		}

		public void RemoveDBWithoutEnv(string home, string dbName, bool ifDBName)
		{
			string dbFileName = home + "/" + dbName + ".db";

			Configuration.ClearDir(home);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.IF_NEEDED;

			BTreeDatabase btreeDB;
			if (ifDBName == false)
			{
				btreeDB = BTreeDatabase.Open(dbFileName, btreeDBConfig);
				btreeDB.Close();
				BTreeDatabase.Remove(dbFileName);
				throw new ExpectedTestException();
			}
			else
			{
				btreeDB = BTreeDatabase.Open(dbFileName, dbName, btreeDBConfig);
				btreeDB.Close();
				BTreeDatabase.Remove(dbFileName, dbName);
				Assert.IsTrue(File.Exists(dbFileName));
				try
				{
					btreeDB = BTreeDatabase.Open(dbFileName, dbName, new BTreeDatabaseConfig());
					btreeDB.Close();
				}
				catch (DatabaseException)
				{
					throw new ExpectedTestException();
				}
			}
		}

		[Test]
		public void TestRemoveDBFromFileInEnv()
		{
			testName = "TestRemoveDBFromFileInEnv";
			SetUpTest(true);

			RemoveDatabase(testHome, testName, true, true);
		}

		[Test]
		public void TestRemoveDBFromEnv()
		{
			testName = "TestRemoveDBFromEnv";
			SetUpTest(true);

			RemoveDatabase(testHome, testName, true, false);
		}

		public void RemoveDatabase(string home, string dbName,
		    bool ifEnv, bool ifDBName)
		{
			string dbFileName = dbName + ".db";

			Configuration.ClearDir(home);

			DatabaseEnvironmentConfig envConfig = 
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseCDB = true;
			envConfig.UseMPool = true;

			DatabaseEnvironment env;
			env = DatabaseEnvironment.Open(home, envConfig);

			try
			{
				BTreeDatabaseConfig btreeDBConfig =
				    new BTreeDatabaseConfig();
				btreeDBConfig.Creation = CreatePolicy.ALWAYS;
				btreeDBConfig.Env = env;
				BTreeDatabase btreeDB = BTreeDatabase.Open(
				    dbFileName, dbName, btreeDBConfig);
				btreeDB.Close();

				if (ifEnv == true && ifDBName == true)
					BTreeDatabase.Remove(dbFileName, dbName, env);
				else if (ifEnv == true)
					BTreeDatabase.Remove(dbFileName, env);
			}
			catch (DatabaseException)
			{
				throw new TestException();
			}
			finally
			{
				try
				{
					BTreeDatabaseConfig dbConfig =
					    new BTreeDatabaseConfig();
					dbConfig.Creation = CreatePolicy.NEVER;
					dbConfig.Env = env;
					BTreeDatabase db = BTreeDatabase.Open(
					    dbFileName, dbName, dbConfig);
					Assert.AreEqual(db.Creation, CreatePolicy.NEVER);
					db.Close();
					throw new TestException();
				}
				catch (DatabaseException)
				{
				}
				finally
				{
					env.Close();
				}
			}
		}

		[Test]
		public void TestRenameDB()
		{
			testName = "TestRenameDB";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" + testName + ".db";
			string btreeDBName = testName;
			string newBtreeDBName = btreeDBName + "1" + ".db";

			try
			{
				BTreeDatabaseConfig btreeDBConfig =
				    new BTreeDatabaseConfig();
				btreeDBConfig.Creation = CreatePolicy.ALWAYS;
				BTreeDatabase btreeDB = BTreeDatabase.Open(
				    btreeDBFileName, btreeDBName, btreeDBConfig);
				btreeDB.Close();
				BTreeDatabase.Rename(btreeDBFileName,
				    btreeDBName, newBtreeDBName);

				BTreeDatabaseConfig dbConfig =
				    new BTreeDatabaseConfig();
				dbConfig.Creation = CreatePolicy.NEVER;
				BTreeDatabase newDB = BTreeDatabase.Open(
				    btreeDBFileName, newBtreeDBName, dbConfig);
				newDB.Close();
			}
			catch (DatabaseException e)
			{
				throw new TestException(e.Message);
			}
			finally
			{
				try
				{
					BTreeDatabaseConfig dbConfig =
					    new BTreeDatabaseConfig();
					dbConfig.Creation = CreatePolicy.NEVER;
					BTreeDatabase db = BTreeDatabase.Open(
					    btreeDBFileName, btreeDBName,
					    dbConfig);
					throw new TestException(testName);
				}
				catch (DatabaseException)
				{
				}
			}
		}

		[Test]
		public void TestRenameDBFile()
		{
			testName = "TestRenameDB";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string newBtreeDBFileName = testHome + "/" +
			    testName + "1.db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			btreeDB.Close();

			BTreeDatabase.Rename(btreeDBFileName,
			    newBtreeDBFileName);
			Assert.IsFalse(File.Exists(btreeDBFileName));
			Assert.IsTrue(File.Exists(newBtreeDBFileName));
		}

		[Test]
		public void TestSync()
		{
			testName = "TestSync";
			SetUpTest(true);
			string btreeDBName = testName + ".db";

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.ForceFlush = true;
			envConfig.UseTxns = true;
			envConfig.UseMPool = true;
			envConfig.UseLogging = true;
			envConfig.LogSystemCfg = new LogConfig();
			envConfig.LogSystemCfg.ForceSync = false;
			envConfig.LogSystemCfg.AutoRemove = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			TransactionConfig txnConfig = new TransactionConfig();
			txnConfig.SyncAction =
			    TransactionConfig.LogFlush.WRITE_NOSYNC;
			Transaction txn = env.BeginTransaction(txnConfig);

			BTreeDatabaseConfig btreeConfig =
			    new BTreeDatabaseConfig();
			btreeConfig.Creation = CreatePolicy.ALWAYS;
			btreeConfig.Env = env;

			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBName, btreeConfig, txn);

			DatabaseEntry key = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("key"));
			DatabaseEntry data = new DatabaseEntry(
			    ASCIIEncoding.ASCII.GetBytes("data"));
			Assert.IsFalse(btreeDB.Exists(key, txn));
			btreeDB.Put(key, data, txn);
			btreeDB.Sync();
			btreeDB.Close(false);
			txn.Commit();
			env.Close();

			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.NEVER;
			using (BTreeDatabase db = BTreeDatabase.Open(
			    testHome + "/" + btreeDBName, dbConfig))
			{
				Assert.IsTrue(db.Exists(key));
			}
		}

		[Test]
		public void TestTruncate()
		{
			testName = "TestTruncate";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			btreeDBConfig.CacheSize =
			    new CacheInfo(0, 30 * 1024, 1);
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);
			DatabaseEntry key;
			DatabaseEntry data;
			for (int i = 0; i < 100; i++)
			{
				key = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				data = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				btreeDB.Put(key, data);
			}
			uint count = btreeDB.Truncate();
			Assert.AreEqual(100, count);
			Assert.IsFalse(btreeDB.Exists(new DatabaseEntry(
			    BitConverter.GetBytes((int)50))));
			btreeDB.Close();
		}

		[Test]
		public void TestTruncateInTxn()
		{
			testName = "TestTruncateInTxn";
			SetUpTest(true);

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseMPool = true;
			envConfig.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			try
			{
				Transaction openTxn = env.BeginTransaction();
				BTreeDatabaseConfig dbConfig =
				    new BTreeDatabaseConfig();
				dbConfig.Creation = CreatePolicy.IF_NEEDED;
				dbConfig.Env = env;
				BTreeDatabase db = BTreeDatabase.Open(
				    testName + ".db", dbConfig, openTxn);
				openTxn.Commit();

				Transaction putTxn = env.BeginTransaction();
				try
				{
					DatabaseEntry key, data;
					for (int i = 0; i < 10; i++)
					{
						key = new DatabaseEntry(
						    BitConverter.GetBytes(i));
						data = new DatabaseEntry(
						    BitConverter.GetBytes(i));
						db.Put(key, data, putTxn);
					}

					putTxn.Commit();
				}
				catch (DatabaseException e)
				{
					putTxn.Abort();
					db.Close();
					throw e;
				}

				Transaction trunTxn = env.BeginTransaction();
				try
				{
					uint count = db.Truncate(trunTxn);
					Assert.AreEqual(10, count);
					Assert.IsFalse(db.Exists(
					    new DatabaseEntry(
					    BitConverter.GetBytes((int)5)), trunTxn));
					trunTxn.Commit();
					db.Close();
				}
				catch (DatabaseException)
				{
					trunTxn.Abort();
					db.Close();
					throw new TestException();
				}
			}
			catch (DatabaseException)
			{
			}
			finally
			{
				env.Close();
			}	
		}

		[Test]
		public void TestTruncateUnusedPages()
		{
			testName = "TestTruncateUnusedPages";
			SetUpTest(true);
			string dbFileName = testHome + "/" + testName + ".db";

			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.ALWAYS;
			dbConfig.PageSize = 512;
			BTreeDatabase db = BTreeDatabase.Open(
			    dbFileName, dbConfig);
			DatabaseEntry key, data;
			for (int i = 0; i < 100; i++)
			{
				key = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				data = new DatabaseEntry(
				    BitConverter.GetBytes(i));
				db.Put(key, data);
			}

			for (int i = 0; i < 80; i++)
				db.Delete(new DatabaseEntry(
				    BitConverter.GetBytes(i)));

			uint count = db.TruncateUnusedPages();
			Assert.LessOrEqual(0, count);

			db.Close();
		}

		[Test]
		public void TestTruncateUnusedPagesWithTxn()
		{
			testName = "TestTruncateUnusedPagesWithTxn";
			SetUpTest(true);

			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseMPool = true;
			envConfig.UseTxns = true;
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    testHome, envConfig);

			BTreeDatabase db;
			try
			{
				Transaction openTxn = env.BeginTransaction();
				try
				{
					BTreeDatabaseConfig dbConfig =
					    new BTreeDatabaseConfig();
					dbConfig.Creation = CreatePolicy.IF_NEEDED;
					dbConfig.Env = env;
					dbConfig.PageSize = 512;
					db = BTreeDatabase.Open(
					    testName + ".db", dbConfig, openTxn);
					openTxn.Commit();
					Assert.AreEqual(512, db.Pagesize);
				}
				catch (DatabaseException e)
				{
					openTxn.Abort();
					throw e;
				}
				
				Transaction putTxn = env.BeginTransaction();
				try
				{
					DatabaseEntry key, data;
					for (int i = 0; i < 100; i++)
					{
						key = new DatabaseEntry(
						    BitConverter.GetBytes(i));
						data = new DatabaseEntry(
						    BitConverter.GetBytes(i));
						db.Put(key, data, putTxn);
					}

					putTxn.Commit();
				}
				catch (DatabaseException e)
				{
					putTxn.Abort();
					db.Close();
					throw e;
				}

				Transaction delTxn = env.BeginTransaction();
				try
				{
					for (int i = 20; i <= 80; i++)
						db.Delete(new DatabaseEntry(
						    BitConverter.GetBytes(i)), delTxn);
					delTxn.Commit();
				}
				catch (DatabaseException e)
				{
					delTxn.Abort();
					db.Close();
					throw e;
				}

				Transaction trunTxn = env.BeginTransaction();
				try
				{
					uint trunPages = db.TruncateUnusedPages(
					    trunTxn);
					Assert.LessOrEqual(0, trunPages);
					trunTxn.Commit();
					db.Close();
				}
				catch (DatabaseException)
				{
					trunTxn.Abort();
					db.Close();
					throw new Exception();
				}
			}
			catch (DatabaseException)
			{
			}
			finally
			{
				env.Close();
			}
		}

		[Test]
		public void TestSalvage()
		{
			testName = "TestSalvage";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string printableOutPut = testHome + "/" +
			    "printableOutPut";
			string inprintableOutPut = testHome + "/" +
			    "inprintableOutPut";

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBConfig);

			DatabaseEntry key;
			DatabaseEntry data;

			for (uint i = 0; i < 10; i++)
			{
				key = new DatabaseEntry(
				    ASCIIEncoding.ASCII.GetBytes(i.ToString()));
				data = new DatabaseEntry(
				    ASCIIEncoding.ASCII.GetBytes(i.ToString()));
				btreeDB.Put(key, data);
			}

			btreeDB.Close();

			StreamWriter sw1 = new StreamWriter(printableOutPut);
			StreamWriter sw2 = new StreamWriter(inprintableOutPut);
			BTreeDatabase.Salvage(btreeDBFileName, btreeDBConfig,
			    true, true, sw1);
			BTreeDatabase.Salvage(btreeDBFileName, btreeDBConfig,
			    false, true, sw2);
			sw1.Close();
			sw2.Close();

			FileStream file1 = new FileStream(printableOutPut,
			    FileMode.Open);
			FileStream file2 = new FileStream(inprintableOutPut,
			    FileMode.Open);
			if (file1.Length == file2.Length)
			{
				int filebyte1 = 0;
				int filebyte2 = 0;
				do
				{
					filebyte1 = file1.ReadByte();
					filebyte2 = file2.ReadByte();
				} while ((filebyte1 == filebyte2) &&
				    (filebyte1 != -1));
				Assert.AreNotEqual(filebyte1, filebyte2);
			}

			file1.Close();
			file2.Close();
		}

		[Test]
		public void TestUpgrade()
		{
			testName = "TestUpgrade";
			SetUpTest(true);

			string srcDBFileName = "../../bdb4.7.db";
			string testDBFileName = testHome + "/bdb4.7.db";

			FileInfo srcDBFileInfo = new FileInfo(srcDBFileName);

			//Copy the file.
			srcDBFileInfo.CopyTo(testDBFileName);
			Assert.IsTrue(File.Exists(testDBFileName));

			BTreeDatabase.Upgrade(testDBFileName,
			    new DatabaseConfig(), true);

			// Open the upgraded database file.
			BTreeDatabase db = BTreeDatabase.Open(
			    testDBFileName, new BTreeDatabaseConfig());
			db.Close();
		}

		[Test, ExpectedException(typeof(TestException))]
		public void TestVerify()
		{
			testName = "TestVerify";
			SetUpTest(true);
			string btreeDBFileName = testHome + "/" +
			    testName + ".db";
			string btreeDBName =
			    Path.GetFileNameWithoutExtension(btreeDBFileName);

			BTreeDatabaseConfig btreeDBConfig =
			    new BTreeDatabaseConfig();
			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
			BTreeDatabase btreeDB = BTreeDatabase.Open(
			    btreeDBFileName, btreeDBName, btreeDBConfig);
			btreeDB.Close();
			btreeDBConfig.Duplicates = DuplicatesPolicy.SORTED;
			BTreeDatabase.Verify(btreeDBFileName, btreeDBConfig,
			    Database.VerifyOperation.NO_ORDER_CHECK);
			try
			{
				BTreeDatabase.Verify(btreeDBFileName,
				    btreeDBConfig,
				    Database.VerifyOperation.ORDER_CHECK_ONLY);
			}
			catch (DatabaseException)
			{
				throw new TestException(testName);
			}
			finally
			{
				BTreeDatabase.Verify(btreeDBFileName,
				    btreeDBName, btreeDBConfig,
				    Database.VerifyOperation.ORDER_CHECK_ONLY);
			}

		}

		[Test]
		public void TestStats()
		{
			testName = "TestStats";
			SetUpTest(true);
			string dbFileName = testHome + "/" +
			    testName + ".db";

			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			ConfigCase1(dbConfig);
			BTreeDatabase db = BTreeDatabase.Open(dbFileName,
			    dbConfig);

			BTreeStats stats = db.Stats();
			ConfirmStatsPart1Case1(stats);

			// Put 500 records into the database.
			PutRecordCase1(db, null);

			stats = db.Stats();
			ConfirmStatsPart2Case1(stats);

			// Delete some data to get some free pages.
			byte[] bigArray = new byte[10240];
			db.Delete(new DatabaseEntry(bigArray));

			db.PrintStats();
			db.PrintFastStats();

			db.Close();
		}

		[Test]
		public void TestStatsInTxn()
		{
			testName = "TestStatsInTxn";
			SetUpTest(true);

			StatsInTxn(testHome, testName, false);
		}

		[Test]
		public void TestStatsWithIsolation()
		{
			testName = "TestStatsWithIsolation";
			SetUpTest(true);

			StatsInTxn(testHome, testName, true);
		}

        [Test]
        public void TestMultipleDBSingleFile()
        {
            testName = "TestMultipleDBSingleFile";
            SetUpTest(true);
            string btreeDBName = testHome + "/" + testName + ".db";

            string dbName = "test";

            /* Create and initialize database object, open the database. */
            BTreeDatabaseConfig btreeDBconfig = new BTreeDatabaseConfig();
            btreeDBconfig.Creation = CreatePolicy.IF_NEEDED;
            btreeDBconfig.ErrorPrefix = testName;
            btreeDBconfig.UseRecordNumbers = true;

            BTreeDatabase btreeDB = BTreeDatabase.Open(btreeDBName, dbName,
                btreeDBconfig);
            btreeDB.Close();
            btreeDB = BTreeDatabase.Open(btreeDBName, dbName + "2",
                btreeDBconfig);
            btreeDB.Close();

            BTreeDatabaseConfig dbcfg = new BTreeDatabaseConfig();
            dbcfg.ReadOnly = true;
            BTreeDatabase newDb = BTreeDatabase.Open(btreeDBName, dbcfg);
            Boolean val = newDb.HasMultiple;
            Assert.IsTrue(val);
            newDb.Close();
        }

		public void StatsInTxn(string home, string name, bool ifIsolation)
		{
			DatabaseEnvironmentConfig envConfig =
			    new DatabaseEnvironmentConfig();
			EnvConfigCase1(envConfig);
			DatabaseEnvironment env = DatabaseEnvironment.Open(
			    home, envConfig);

			Transaction openTxn = env.BeginTransaction();
			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			ConfigCase1(dbConfig);
			dbConfig.Env = env;
			BTreeDatabase db = BTreeDatabase.Open(name + ".db",
			    dbConfig, openTxn);
			openTxn.Commit();

			Transaction statsTxn = env.BeginTransaction();
			BTreeStats stats;
			BTreeStats fastStats;
			if (ifIsolation == false)
			{
				stats = db.Stats(statsTxn);
				fastStats = db.FastStats(statsTxn);
			}
			else
			{
				stats = db.Stats(statsTxn, Isolation.DEGREE_ONE);
				fastStats = db.FastStats(statsTxn, 
				    Isolation.DEGREE_ONE);
			}
			ConfirmStatsPart1Case1(stats);

			// Put 500 records into the database.
			PutRecordCase1(db, statsTxn);

			if (ifIsolation == false)
				stats = db.Stats(statsTxn);
			else
				stats = db.Stats(statsTxn, Isolation.DEGREE_TWO);
			ConfirmStatsPart2Case1(stats);

			// Delete some data to get some free pages.
			byte[] bigArray = new byte[10240];
			db.Delete(new DatabaseEntry(bigArray), statsTxn);
			if (ifIsolation == false)
				stats = db.Stats(statsTxn);
			else
				stats = db.Stats(statsTxn, Isolation.DEGREE_THREE);
			ConfirmStatsPart3Case1(stats);

			db.PrintStats(true);
			Assert.AreEqual(0, stats.EmptyPages);

			statsTxn.Commit();
			db.Close();
			env.Close();
		}

		public void EnvConfigCase1(DatabaseEnvironmentConfig cfg)
		{
			cfg.Create = true;
			cfg.UseTxns = true;
			cfg.UseMPool = true;
			cfg.UseLogging = true;
		}

		public void ConfigCase1(BTreeDatabaseConfig dbConfig)
		{
			dbConfig.Creation = CreatePolicy.IF_NEEDED;
			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
			dbConfig.PageSize = 4096;
			dbConfig.MinKeysPerPage = 10;
		}

		public void PutRecordCase1(BTreeDatabase db, Transaction txn)
		{
			byte[] bigArray = new byte[10240];
			for (int i = 0; i < 100; i++)
			{
				if (txn == null)
					db.Put(new DatabaseEntry(BitConverter.GetBytes(i)),
					    new DatabaseEntry(BitConverter.GetBytes(i)));
				else
					db.Put(new DatabaseEntry(BitConverter.GetBytes(i)),
					    new DatabaseEntry(BitConverter.GetBytes(i)), txn);
			}
			for (int i = 100; i < 500; i++)
			{
				if (txn == null)
					db.Put(new DatabaseEntry(bigArray),
					    new DatabaseEntry(bigArray));
				else
					db.Put(new DatabaseEntry(bigArray),
					    new DatabaseEntry(bigArray), txn);
			}
		}

		public void ConfirmStatsPart1Case1(BTreeStats stats)
		{
			Assert.AreEqual(1, stats.EmptyPages);
			Assert.AreNotEqual(0, stats.LeafPagesFreeBytes);
			Assert.AreEqual(1, stats.Levels);
			Assert.AreNotEqual(0, stats.MagicNumber);
			Assert.AreEqual(1, stats.MetadataFlags);
			Assert.AreEqual(10, stats.MinKey);
			Assert.AreEqual(2, stats.nPages);
			Assert.AreEqual(4096, stats.PageSize);
			Assert.AreEqual(9, stats.Version);
		}

		public void ConfirmStatsPart2Case1(BTreeStats stats)
		{
			Assert.AreNotEqual(0, stats.DuplicatePages);
			Assert.AreNotEqual(0, stats.DuplicatePagesFreeBytes);
			Assert.AreNotEqual(0, stats.InternalPages);
			Assert.AreNotEqual(0, stats.InternalPagesFreeBytes);
			Assert.AreNotEqual(0, stats.LeafPages);
			Assert.AreEqual(500, stats.nData);
			Assert.AreEqual(101, stats.nKeys);
			Assert.AreNotEqual(0, stats.OverflowPages);
			Assert.AreNotEqual(0, stats.OverflowPagesFreeBytes);
		}

		public void ConfirmStatsPart3Case1(BTreeStats stats)
		{
			Assert.AreNotEqual(0, stats.FreePages);
		}

		private int dbIntCompare(DatabaseEntry dbt1,
		    DatabaseEntry dbt2)
		{
			int a, b;
			a = BitConverter.ToInt32(dbt1.Data, 0);
			b = BitConverter.ToInt32(dbt2.Data, 0);
			return a - b;
		}

		private uint dbPrefixCompare(DatabaseEntry dbt1,
		    DatabaseEntry dbt2)
		{
			uint cnt, len;

			len = Math.Min((uint)dbt1.Data.Length, (uint)dbt2.Data.Length);
			for (cnt = 0; cnt < len; cnt++)
			{
				if (dbt1.Data[cnt] != dbt2.Data[cnt])
					return cnt + 1;
			}
			if (dbt1.Data.Length > dbt2.Data.Length)
				return (uint)dbt2.Data.Length + 1;
			else if (dbt1.Data.Length < dbt2.Data.Length)
				return (uint)dbt1.Data.Length + 1;
			else
				return (uint)dbt1.Data.Length;
		}

		public void SetUpEnvAndTxn(string home,
		    out DatabaseEnvironment env, out Transaction txn)
		{
			DatabaseEnvironmentConfig envConfig =
				new DatabaseEnvironmentConfig();
			envConfig.Create = true;
			envConfig.UseTxns = true;
			envConfig.UseMPool = true;
			env = DatabaseEnvironment.Open(home, envConfig);
			txn = env.BeginTransaction();
		}

		public void OpenBtreeDB(DatabaseEnvironment env,
		    Transaction txn, string dbFileName,
		    out BTreeDatabase db)
		{
			BTreeDatabaseConfig dbConfig =
			    new BTreeDatabaseConfig();
			dbConfig.Creation = CreatePolicy.IF_NEEDED;
			if (env != null)
			{
				dbConfig.Env = env;
				dbConfig.NoMMap = false;
				db = BTreeDatabase.Open(dbFileName, dbConfig, txn);
			}
			else
			{
				db = BTreeDatabase.Open(dbFileName, dbConfig);
			}
		}

                private BTreeDatabase GetMultipleDB(
                    string filename, string dbname, BTreeDatabaseConfig cfg) {
                        BTreeDatabase ret;
                        DatabaseEntry data, key;
 
                        ret = BTreeDatabase.Open(filename, dbname, cfg);
                        key = null;
                        if (cfg.UseRecordNumbers) {
                                /* 
                                 * Dups aren't allowed with record numbers, so
                                 * we have to put different data.  Also, record
                                 * numbers start at 1, so we do too, which makes 
                                 * checking results easier.
                                 */
                                for (int i = 1; i < 100; i++) {
                                        key = new DatabaseEntry(
                                            BitConverter.GetBytes(i));
                                        data = new DatabaseEntry(
                                            BitConverter.GetBytes(i));
                                        ret.Put(key, data);
                                }

                                key = new DatabaseEntry(
                                    BitConverter.GetBytes(100));
                                data = new DatabaseEntry();
                                data.Data = new byte[111];
                                for (int i = 0; i < 111; i++)
                                        data.Data[i] = (byte)i;
                                        ret.Put(key, data);
                        } else {
                                for (int i = 0; i < 100; i++) {
                                        if (i % 10 == 0)
                                        key = new DatabaseEntry(
                                            BitConverter.GetBytes(i));
                                        data = new DatabaseEntry(
                                            BitConverter.GetBytes(i));
                                        /* Don't put nulls into the db. */
                                        Assert.IsFalse(key == null);
                                        Assert.IsFalse(data == null);
                                        ret.Put(key, data);
                                }
                                
                                if (cfg.Duplicates == DuplicatesPolicy.UNSORTED) {
                                        /* Add in duplicates to check GetBothMultiple */
                                        key = new DatabaseEntry(
                                            BitConverter.GetBytes(100));
                                        data = new DatabaseEntry(
                                            BitConverter.GetBytes(100));
                                        for (int i = 0; i < 10; i++)
                                                ret.Put(key, data);

                                        /*
                                         * Add duplicates to check GetMultiple 
                                         * with given buffer size. 
                                         */
                                        for (int i = 101; i < 1024; i++) {
                                                key = new DatabaseEntry(
                                                    BitConverter.GetBytes(101));
                                                data = new DatabaseEntry(
                                                    BitConverter.GetBytes(i));
                                                ret.Put(key, data);
                                        }

                                        key = new DatabaseEntry(
                                            BitConverter.GetBytes(102));
                                        data = new DatabaseEntry();
                                        data.Data = new byte[112];
                                        for (int i = 0; i < 112; i++)
			                     data.Data[i] = (byte)i;
                                        ret.Put(key, data);
                                }
                        }
	               return ret;	
                }

		public static void Confirm(XmlElement xmlElem,
		    BTreeDatabase btreeDB, bool compulsory)
		{
			DatabaseTest.Confirm(xmlElem, btreeDB, compulsory);
			Configuration.ConfirmDuplicatesPolicy(xmlElem,
			    "Duplicates", btreeDB.Duplicates, compulsory);
			Configuration.ConfirmUint(xmlElem, "MinKeysPerPage",
			    btreeDB.MinKeysPerPage, compulsory);
			/*
			 * BTreeDatabase.RecordNumbers is the value of
			 * BTreeDatabaseConfig.UseRecordNumbers.
			 */
			Configuration.ConfirmBool(xmlElem, "UseRecordNumbers",
			    btreeDB.RecordNumbers, compulsory);
			/*
			 * BTreeDatabase.ReverseSplit is the value of
			 * BTreeDatabaseConfig.NoReverseSplitting.
			 */
			Configuration.ConfirmBool(xmlElem, "NoReverseSplitting",
			    btreeDB.ReverseSplit, compulsory);
			Assert.AreEqual(DatabaseType.BTREE, btreeDB.Type);
			string type = btreeDB.ToString();
			Assert.IsNotNull(type);
		}
	}
}

